2016-12-14 96 views
0

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

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

这听起来像是你的类的设计问题。 CustomException本身不应该记录到Crashlyrics。相反,如果有必要,应该由订户负责记录。 –

+0

@DavidRawson因此自定义异常应该由其他人订阅,或者我应该将自定义记录器类传递给异常,将异常记录到任何平台上,例如Crashlytics,并将其用于测试? –

+0

感谢您的编辑 - 让问题更清晰。一个好的解决方案是使用包装类来包装Crashlytics中的静态方法。然后传入包装类作为订阅者的依赖关系。看[这个答案](http:// stackoverflow。com/a/29841824/5241933) –

回答

2

你遇到的是什么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

另一种方法是使用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