Bohsen
Bohsen

Reputation: 4350

Android Interactor testing

I have the following class for which I'm trying to figure out how to write a unit test:

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

Using Retrofit I'm calling my webservice to get authenticated and the webservice should return an Authtoken for later use. I just can't grasp how to test this. I've tried creating a solution using Mockito, but I just can't figure out how to test the logic of the login-method. Are there any experts on Mockito/Retrofit who could guide me a bit closer to a working solution?

Upvotes: 1

Views: 1054

Answers (1)

Maciej Kowalski
Maciej Kowalski

Reputation: 26562

There are quite a few things that i would refactor before attempting any unit testing:

1) Create an explicit Callback class from the anonymous one created inside the enqueue method.. lets call it 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();
           ...
    }
}

Now you perform isolated unit testing on those two methods logic.

2) Move the static method calls to package level methods. You would end up with something like this:

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) Unit test the login method

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

Upvotes: 4

Related Questions