Reputation: 4350
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
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