2017-04-20 97 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