如何在抛出异常

时间:2016-12-14 04:51:53

标签: android unit-testing mocking crashlytics

我正在使用MVP架构来构建我的应用程序。我的演示者调用DataManager,它负责从网络或数据库获取数据。当我使用RxJava时,我在Presenter中订阅Observers并将适当的状态传递给UI。我的服务层有Android上下文,它也会创建一个我自己类型的异常,它也引用了Context。

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;
    }

2 个答案:

答案 0 :(得分:4)

您遇到的问题是some argue是静态方法的问题。静态方法logException()在任何地方添加似乎都无害,但它实际上隐藏了一个真正的依赖。由于现在需要该类运行依赖项,因此很难对您的类进行测试。

一个好的解决方案是使用非静态方法创建包装类。如果我们将this answer中的解决方案应用于您的代码,它将看起来像这样:

public class CrashLyticsWrapper { 
    public CrashLyticsWrapper() {} 

    public void logException(Throwable t) {
         CrashLytics.logException(t);
    }
}

然后可以在构造函数中将其作为依赖项传递给需要它的类。由于它是一个非静态依赖项和您现在控制的类,因此您可以轻松地对其进行模拟并在必要时进行验证。

一个单独的问题:异常就像值对象,在大多数情况下不应该具有静态或非静态依赖性。做这样的事情:

public CustomException extends RuntimeException() {
     public CustomException() {
          Crashlytics.logException(this); //don't do this!
     }
}

使代码变得脆弱和不稳定。您可以从构造函数中看到,您从子类别异常中覆盖了有一个原因字段,一个消息字段,以及它的相关信息。一个例外的良好范围。如果需要添加与异常一起使用的功能,则应编写单独的错误处理程序,将异常作为数据或参数。这符合SOLID中的“单一责任”原则。

答案 1 :(得分:2)

另一种方法可能是使用powermock模拟包含静态方法的Crashlytics类。

测试向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