如何在抛出的异常中模拟 Crashlytics 静态方法
How to mock a Crashlytics static method inside a thrown Exception
我正在使用 MVP 架构来构建我的应用程序。我的演示者调用一个 DataManager,它负责从网络或数据库中获取数据。当我使用 RxJava 时,我在 Presenter 中订阅 Observers 并将适当的状态传递给 UI。我的服务层有 Android 上下文,它还会创建一个我自己类型的异常,它也有对上下文的引用。
if (isNetworkConnected()) {
final Call<ServiceResponse<AppVersion>> call = mService.getAppVersion();
try {
final Response<ServiceResponse<AppVersion>> response = call.execute();
if (response.isSuccessful()) {
final ServiceResponse<AppVersion> serviceResponse = response.body(); response.body();
if (serviceResponse.isSuccess()) {
subscriber.onNext(serviceResponse.getData());
} else {
subscriber.onError(new CustomException(mContext, response.code(), response.message(), serviceResponse.getErrorList()));
}
} else {
subscriber.onError(new CustomException(mContext, response.code(), response.message(), response.errorBody().string()));
}
} catch (IOException e) {
e.printStackTrace();
subscriber.onError(e);
} finally {
subscriber.onCompleted();
}
} else {
subscriber.onError(new NoInternetException());
}
我的 CustomException 还在 Crashlytics 中记录了崩溃。当我对该代码进行单元测试时,我收到 Crashlytics 未初始化的异常。所以我需要模拟Crashlytics的静态方法logException
。但是我应该如何传递这个模拟对象,因为演示者不接受这个对象?
public staticErrorType getErrorType(Throwable throwable) {
//409: Not handled as its a conflict response code and comes in PUT/POST
if (throwable instanceof IOException) {
return ErrorType.NO_INTERNET;
} else if (throwable instanceof CustomException) {
final int errorCode = ((CustomException) throwable).mErrorCode;
if (errorCode == 404) {
return ErrorType.NOT_FOUND;
} else if (errorCode == 401) {
return ErrorType.UNAUTORISED;
} else if (errorCode == 403) {
return ErrorType.FORBIDDEN;
} else if (errorCode == 500 || errorCode == 502) {
return ErrorType.NO_SERVER_TRY_AGAIN;
} else if (errorCode > 500 && errorCode < 599) {
return ErrorType.NO_SERVER_TRY_LATER;
} else if (errorCode == 1000) {
return ErrorType.NO_COURSE_ENROLLED;
} else if (errorCode == 1001) {
return ErrorType.NO_COURSE_STARTED;
}
}
if (throwable != null) {
Crashlytics.logException(throwable);
}
return ErrorType.SOME_THING_WENT_WRONG;
}
你遇到的是什么some argue是静态方法的问题。静态方法 logException()
似乎无处不在,但它实际上隐藏了真正的依赖性。由于现在 class 到 运行 需要这种依赖性,这使您的 class 难以测试。
一个好的解决方案是使用非静态方法创建包装器 class。如果我们将 this answer 中的解决方案应用到您的代码中,它将看起来像这样:
public class CrashLyticsWrapper {
public CrashLyticsWrapper() {}
public void logException(Throwable t) {
CrashLytics.logException(t);
}
}
然后可以将其作为依赖项传递给需要它的 classes。由于它是一个非静态依赖项并且是您现在控制的 class,因此您可以轻松模拟它并在必要时对其进行验证。
一个单独的问题:异常就像值对象,在大多数情况下不应该有静态或非静态的依赖关系。做这样的事情:
public CustomException extends RuntimeException() {
public CustomException() {
Crashlytics.logException(this); //don't do this!
}
}
生成脆弱且无法测试的代码。您可以从构造函数中看到,您从 sub-classing 异常中覆盖了一个用于原因的字段,一个用于消息的字段,仅此而已。一个很好的例外范围。如果您需要添加伴随异常的功能,您应该编写单独的错误处理程序,将异常作为数据或参数。这符合SOLID.
中的"single responsiblity"原则
另一种方法可能是使用 powermock 模拟包含静态方法的 Crashlytics class。
测试向 Crashlytics 发送日志的函数可能如下所示
@RunWith(PowerMockRunner.class)
@PrepareForTest({Crashlytics.class})
@PowerMockIgnore({"javax.net.ssl.*"})
public class presenterTest{
@Test public void testFunctionWithCrashlyticsCall() throws Exception{
PowerMockito.mockStatic(Crashlytics.class);
...
assertEquals(..)
}
}
可在此处找到相关文档:
https://github.com/powermock/powermock/wiki/Mockito#mocking-static-method
我正在使用 MVP 架构来构建我的应用程序。我的演示者调用一个 DataManager,它负责从网络或数据库中获取数据。当我使用 RxJava 时,我在 Presenter 中订阅 Observers 并将适当的状态传递给 UI。我的服务层有 Android 上下文,它还会创建一个我自己类型的异常,它也有对上下文的引用。
if (isNetworkConnected()) {
final Call<ServiceResponse<AppVersion>> call = mService.getAppVersion();
try {
final Response<ServiceResponse<AppVersion>> response = call.execute();
if (response.isSuccessful()) {
final ServiceResponse<AppVersion> serviceResponse = response.body(); response.body();
if (serviceResponse.isSuccess()) {
subscriber.onNext(serviceResponse.getData());
} else {
subscriber.onError(new CustomException(mContext, response.code(), response.message(), serviceResponse.getErrorList()));
}
} else {
subscriber.onError(new CustomException(mContext, response.code(), response.message(), response.errorBody().string()));
}
} catch (IOException e) {
e.printStackTrace();
subscriber.onError(e);
} finally {
subscriber.onCompleted();
}
} else {
subscriber.onError(new NoInternetException());
}
我的 CustomException 还在 Crashlytics 中记录了崩溃。当我对该代码进行单元测试时,我收到 Crashlytics 未初始化的异常。所以我需要模拟Crashlytics的静态方法logException
。但是我应该如何传递这个模拟对象,因为演示者不接受这个对象?
public staticErrorType getErrorType(Throwable throwable) {
//409: Not handled as its a conflict response code and comes in PUT/POST
if (throwable instanceof IOException) {
return ErrorType.NO_INTERNET;
} else if (throwable instanceof CustomException) {
final int errorCode = ((CustomException) throwable).mErrorCode;
if (errorCode == 404) {
return ErrorType.NOT_FOUND;
} else if (errorCode == 401) {
return ErrorType.UNAUTORISED;
} else if (errorCode == 403) {
return ErrorType.FORBIDDEN;
} else if (errorCode == 500 || errorCode == 502) {
return ErrorType.NO_SERVER_TRY_AGAIN;
} else if (errorCode > 500 && errorCode < 599) {
return ErrorType.NO_SERVER_TRY_LATER;
} else if (errorCode == 1000) {
return ErrorType.NO_COURSE_ENROLLED;
} else if (errorCode == 1001) {
return ErrorType.NO_COURSE_STARTED;
}
}
if (throwable != null) {
Crashlytics.logException(throwable);
}
return ErrorType.SOME_THING_WENT_WRONG;
}
你遇到的是什么some argue是静态方法的问题。静态方法 logException()
似乎无处不在,但它实际上隐藏了真正的依赖性。由于现在 class 到 运行 需要这种依赖性,这使您的 class 难以测试。
一个好的解决方案是使用非静态方法创建包装器 class。如果我们将 this answer 中的解决方案应用到您的代码中,它将看起来像这样:
public class CrashLyticsWrapper {
public CrashLyticsWrapper() {}
public void logException(Throwable t) {
CrashLytics.logException(t);
}
}
然后可以将其作为依赖项传递给需要它的 classes。由于它是一个非静态依赖项并且是您现在控制的 class,因此您可以轻松模拟它并在必要时对其进行验证。
一个单独的问题:异常就像值对象,在大多数情况下不应该有静态或非静态的依赖关系。做这样的事情:
public CustomException extends RuntimeException() {
public CustomException() {
Crashlytics.logException(this); //don't do this!
}
}
生成脆弱且无法测试的代码。您可以从构造函数中看到,您从 sub-classing 异常中覆盖了一个用于原因的字段,一个用于消息的字段,仅此而已。一个很好的例外范围。如果您需要添加伴随异常的功能,您应该编写单独的错误处理程序,将异常作为数据或参数。这符合SOLID.
中的"single responsiblity"原则另一种方法可能是使用 powermock 模拟包含静态方法的 Crashlytics class。
测试向 Crashlytics 发送日志的函数可能如下所示
@RunWith(PowerMockRunner.class)
@PrepareForTest({Crashlytics.class})
@PowerMockIgnore({"javax.net.ssl.*"})
public class presenterTest{
@Test public void testFunctionWithCrashlyticsCall() throws Exception{
PowerMockito.mockStatic(Crashlytics.class);
...
assertEquals(..)
}
}
可在此处找到相关文档: https://github.com/powermock/powermock/wiki/Mockito#mocking-static-method