2017-04-20 67 views
0

我有以下类,我正在试图找出如何写一个单元测试:Android的交互器测试

@EBean 
public class LoginInteractorImpl implements LoginInteractor { 

    private final static String LOGIN_INTERACTOR_THREAD_ID = "LOGIN_INTERACTOR_WORKER"; 

    private UserRepo mUserRepository; 
    private Call<ResponseBody> mCall; 

    @Override 
    @Background(id = LOGIN_INTERACTOR_THREAD_ID) 
    public void login(final String username, final String password, final OnLoginFinishedListener listener) { 

     mUserRepository = RetrofitHelper.createClient(UserRepo.class); 

     mCall = mUserRepository.doLogin(Credentials.basic(username, password)); 

     mCall.enqueue(new Callback<ResponseBody>() { 
      @Override 
      public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { 
       final int code = response.code(); 
       if (code == 200) { 
        listener.onSuccess(response.headers().get("")); 
       } else if (code == 401) { 
        listener.invalidCredentialsFailure(); 
       } else if (code >= 400 && code < 500) { 
        listener.invalidCredentialsFailure(); 
       } else if (code >= 500 && code < 600) { 
        listener.noServerResponseFailure(); 
       } else { 
        APIError error = ErrorUtils.parseError(response); 
        listener.onError("Unexpected response: code:" 
         + error.getStatusCode() 
         + "message: " + error.getMessage()); 
       } 
      } 

      @Override 
      public void onFailure(Call<ResponseBody> call, Throwable t) { 
       if (t instanceof IOException) { 
        listener.noNetworkFailure(); 
       } else if(call.isCanceled()) { 
        Log.e(TAG, "request was aborted"); 
       } else { 
        listener.onError(t.getMessage()); 
       } 
      } 
     }); 
    } 

    @Override 
    public void cancel() { 
    mCall.cancel(); 
    BackgroundExecutor.cancelAll(LOGIN_INTERACTOR_THREAD_ID, true); 
    } 
} 

使用改装我打电话我的web服务得到验证,web服务应该返回一个Authtoken供以后使用。我无法掌握如何测试这个。我试过使用Mockito创建一个解决方案,但我无法弄清楚如何测试登录方法的逻辑。有没有Mockito/Retrofit的专家能指导我更接近一个可行的解决方案?

回答

2

有相当多的是,我会尝试任何单元测试之前,重构的几件事情:

1)创建一个从enqueue方法中创建的匿名一个明确的回调类..可以称之为LoginCallback

public class LoginCallback implements Callback{ 
    @Override 
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { 
      final int code = response.code(); 
      if (code == 200) { 
      ... 
    } 

    @Override 
    public void onFailure(Call<ResponseBody> call, Throwable t) { 
     if (t instanceof IOException) { 
      listener.noNetworkFailure(); 
      ... 
    } 
} 

现在你对这两个方法逻辑进行隔离单元测试。

2)将静态方法调用移至包级别方法。你最终会是这样的:

public class LoginInteractorImpl implements LoginInteractor { 

    public void login(final String username, final String password, final OnLoginFinishedListener listener) { 

    mUserRepository = createClient(); 

    mCall = mUserRepository.doLogin(getCredentials(username,password)); 

    mCall.enqueue(new LoginCallback()); 
    } 

    UserRepo createClient(){ 
     RetrofitHelper.createClient(UserRepo.class) 
    } 

    Credentials getCredentials(String username, String password){ 
     return Credentials.basic(username, password) 
    } 
} 

3)单元测试login方法

@RunWith(JunitMockitoRunner.class) 
public class TestClass{ 

    @Spy 
    private LoginInteractorImpl loginInterceptor= new LoginInteractorImpl(); 

    @Mock 
    private UserRepo mUserRepositoryMock; 

    @Mock 
    private Call<ResponseBody> mCallMock; 

    @Mock 
    private Credentials credentialsMock; 

    public void shouldEnqueueWhenLogin(){ 
     // Arrange 
     String username = "name"; 
     String password = "pass"; 
     Mockito.doReturn(mUserRepositoryMock).when(loginInterceptor).createClient(); 

     Mockito.doReturn(credentialsMock).when(loginInterceptor).when(getCredentials(username, password)); 

     Mockito.doReturn(mCallMock).when(mUserRepositoryMock).doLogin(credentialsMock); 

     // Act 
     loginInterceptor.login(username, password); 

     // Assert that proper callback has been passed to enqueue 
     verify(mCallMock).enqueue(Mockito.any(LoginCallback.class)); 
    } 
} 
+0

非常感谢你这一点。使它更清洁,更容易测试。但我仍然有一个问题。我正在使用Android Annotations来创建我的交互类的最后一个类。这不适用于@Spy。有没有办法解决这个问题,或者我应该放弃Android Annotations并对我自己进行线程化? – Bohsen

+0

我明白了。那么我会建议PowerMockito。如果你真的不能正确地重构你的代码,它应该是最后的手段,但它会为你做诡计。按照这个教程https://github.com/powermock/powermock/wiki/MockFinal和我的代码的一个小的变化,你应该能够把它关闭。让我知道,如果你偶然发现一些问题。 –

+0

我无法让PowerMockito与Robolectric一起工作,所以我最终得到了一个使用静态初始化器的可怕解决方案。你看到以下任何问题: 私人静态UserRepo mUserRepository; ... static {mUserRepository = createClient();} 然后我用setter来替换mUserRepository和mock。 – Bohsen