How to write a proper android Unit Test on the presenter layer

Viewed 4623

I was under the assumption that writing unit tests would be a lot easier when using the Model-View-Presenter (MVP) pattern but I'm not sure how to go forward with even beginning. I've added dependencies to start writing unit tests with Roboelectric and Mockito to mock all the dependencies being used in my presenter.

For some reference, here is an example of my presenter for login:

public class LoginPresenterImpl implements ILoginPresenter {

@Inject
Bus bus;

@Inject
ISharedPreferencesRepository mSharedPreferencesRepository;

private final String LOG_TAG = "LOGIN_PRESENTER";
private ILoginView loginView;
private LoginInteractorImpl loginInteractor;
private long mLastClickTime = 0;

public LoginPresenterImpl() {
    MyApplication.getObjectGraph().inject(this);
    this.loginInteractor = new LoginInteractorImpl();
}

@Override
public void setLoginView(ILoginView loginView) {
    this.loginView = loginView;

    if(mSharedPreferencesRepository.isLoggedIn()) {
        Log.i(LOG_TAG, "User logged in already");
        this.loginView.navigateToHome();
    }
}

@Override
public void validateCredentials(String username, String password) {
    if(SystemClock.elapsedRealtime() - mLastClickTime < 1000)
        return;
    mLastClickTime = SystemClock.elapsedRealtime();

    if(username.equals("") || username == null) {
        loginView.setUsernameError();
        return;
    } else if(password.equals("") || password == null){
        loginView.setPasswordError();
        return;
    }

    loginView.showProgress();
    loginInteractor.login(username, password, this);
}

@Override
public void onUsernameError() {
    loginView.setUsernameError();
    loginView.hideProgress();
}

@Override
public void onPasswordError() {
    loginView.setPasswordError();
    loginView.hideProgress();
}

@Subscribe
@Override
public void onSuccess(LoginEvent event) {
    if (event.getIsSuccess()) {
        mSharedPreferencesRepository.setLogin(true);
        mSharedPreferencesRepository.setFirstTime(true);
        mSharedPreferencesRepository.setUserId(event.getUserId());

        loginView.navigateToHome();
        loginView.hideProgress();
    }
}

Now onto my unit test (attempt):

First, correct me if I'm wrong, but in order to begin writing my test cases, I'll need to Mock a few objects: Bus, ISharedPreferencesRepository, ILoginView, LoginInteractorImpl.

I ended up with something like this:

@RunWith(MockitoJUnitRunner.class)
public class LoginPresenterImplTest {

    @Mock
    private ILoginView view;

    @Mock
    private LoginInteractorImpl interactor;

    @Mock
    private LoginPresenterImpl presenter;

    @Before
    public void setUp(){

    }


    @Test
    public void shouldShowErrorMessageWhenUsernameIsEmpty() throws Exception {
        when(view.getUsername()).thenReturn("");
        when(view.getPassword()).thenReturn("test");
        presenter.validateCredentials(view.getUsername(), view.getPassword());
    }
}

I'm a bit lost at the next steps to take. I'm trying to verify that the method loiginView.setOnUsernameError() will be called in this scenario by adding verify(view, times(1)).setUsernameError(); but this fails my test.

1 Answers
Related