How to Do TDD in Android — Part 2: Unit Tests
Continuing the TDD in Android series — in this installment we write our first unit tests.
As we saw in Part 1, we have a project set up using the MVP pattern. The app will be a simple login screen with the following behavior:
- Maximum 3 login attempts
- Username/password validation
Creating the presentation layer
First, create a login package in the project and inside it a LoginPresenter class:
public class LoginPresenter {
}
With the cursor on the class name, press SHIFT + CTRL + T — Android Studio will ask you to create a new test called LoginPresenterTest.
Now we have our presenter and its test class. Time to write the tests.
Writing the first tests
The first thing to test: whether the maximum number of login attempts has been exceeded. In LoginPresenterTest:
@Test
public void checkIfLoginAttemptIsExceeded() {
LoginPresenter loginPresenter = new LoginPresenter();
Assert.assertEquals(1, loginPresenter.newLoginAttempt());
Assert.assertEquals(2, loginPresenter.newLoginAttempt());
Assert.assertEquals(3, loginPresenter.newLoginAttempt());
Assert.assertTrue(loginPresenter.isLoginAttemptExceeded());
}
The methods newLoginAttempt and isLoginAttemptExceeded will be highlighted in red — they don’t exist yet. This is the first step in the TDD cycle (red → refactor → green).
Next, create those methods in LoginPresenter:
public class LoginPresenter {
public int newLoginAttempt() {
return 0;
}
public boolean isLoginAttemptExceeded() {
return false;
}
}
If you run the test now it fails — the methods exist but don’t do anything yet. Now implement them:
public class LoginPresenter {
private int currentLoginAttempt = 0;
private static final int MAX_LOGIN_ATTEMPT = 3;
public int newLoginAttempt() {
return ++currentLoginAttempt;
}
public boolean isLoginAttemptExceeded() {
return currentLoginAttempt >= MAX_LOGIN_ATTEMPT;
}
}
Run the test again — it passes and turns green.
Never lose sight of the green — @flipper83
Exercise: implement the inverse test yourself:
@Test
public void checkIfLoginAttemptIsNotExceeded() {
// your implementation here
}
Checking username and password
Now let’s verify that the entered credentials are correct:
@Test
public void checkUserAndPasswordIsCorrect() {
LoginPresenter loginPresenter = new LoginPresenter();
Assert.assertTrue(loginPresenter.checkUserPassword("user", "password"));
}
The checkUserPassword method is red — implement it:
public boolean checkUserPassword(String user, String password) {
boolean ret = true;
if (isLoginAttemptExceeded()) {
ret = false;
} else if (user.equals(USER) && password.equals(PASSWORD)) {
ret = true;
} else {
ret = false;
newLoginAttempt();
}
return ret;
}
The complete LoginPresenter class:
public class LoginPresenter {
private int currentLoginAttempt = 0;
private static final int MAX_LOGIN_ATTEMPT = 3;
private static final String USER = "user";
private static final String PASSWORD = "password";
public int newLoginAttempt() {
return ++currentLoginAttempt;
}
public boolean isLoginAttemptExceeded() {
return currentLoginAttempt >= MAX_LOGIN_ATTEMPT;
}
public boolean checkUserPassword(String user, String password) {
boolean ret = true;
if (isLoginAttemptExceeded()) {
ret = false;
} else if (user.equals(USER) && password.equals(PASSWORD)) {
ret = true;
} else {
ret = false;
newLoginAttempt();
}
return ret;
}
}
Notice something: by using MVP, we’ve fully implemented and tested the logic of a login screen before writing a single line of UI code.
Download the current state of the project: https://github.com/jamontes79/TDD_Ejemplo/tree/f7549820c11db027fc70196c828ccb8bcb5e33ac
In the next installment we’ll build the UI and work with Mockito. What do you think of TDD so far?