Test Driven Development on Android

Test Driven Development is one of many ways to achieve high code quality and avoid regression issues, but on Android, it is a little more complicated. Activities, Fragments, Services and other Android components have been hard to test. I say “have been” because thanks to external libraries, it is easier than before.

Test Driven Development, or TDD: What is this? Simply put, it is a software development style that uses a very short development process and repetitions.

How to do it?

  1. Split your work into small features.
  2. Write all requirements for this feature.
  3. Write as many tests as you can and see it fails (red).
  4. Implement solution as simple as possible. Don’t care about code quality.
  5. Check tests until all pass. (green)
  6. Refactor your code until you’re happy with its quality.

Where are the benefits?

  • Developers write minimal source code. No additional unused methods will be introduced because YAGNI (you ain’t gonna need it).
  • You can build a quick MVP.
  • TDD resolves problems with regression. We know that at the end of feature development, every test was green.
  • Source code will be more maintainable and leaner.
  • Kill procrastination. You have to write tests and there isn’t a short cut.

Dependencies in gradle:

testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-all:1.10.19'
testCompile 'org.robolectric:robolectric:3.1.1'

JUnit4

Finally, Android can work with unit tests 4th version and we can finally use annotations. @Test, @Before, @After annotations can improve our work and open new possibilities. New assertions and expectations for Exceptions.

assertEquals(Object[] expected, Object[] actual)
assertEquals(String message, Object[] expected, Object[] actual)
assumeThat(T actual, org.hamcrest.Matcher<T> matcher)
@Test(expected = IOException.class)

Mockito

Do you want to test communication with the server? You have to use Mockito. Thanks to this library you can mock all methods in your application and return the expected result. Isolated code: this is our goal. We shouldn’t test the server, Android classes, or internet connection. We should check how the application will work with this or that kind of data. For example, what happens when a url for a profile picture is null, or the status of an object is unknown.

public interface RestClient {    
     @POST("registration/")
     RegistrationResponse registerUser(@Body User user);
}

Simple example. We have a simple Retrofit interface with one request. Our AsyncTask class is using this method and we want to check what will happen when the response is correct and when it isn’t.

@RunWith(MockitoJUnitRunner.class)
public class RegistrationRequestTest {
     @Mock
     RestClient mClient;

     public void testSuccessResponse() {
          RegistrationResponse successResponse = new RegistrationResponse();
          // Here you have to setup all response fields;
          ...
          Mockito.when(mClient.registerUser(Matchers.anyObject())).thenReturn(successResponse);
          new RegistrationRequest(mClient).execute(registrationData);
     }

     public void testException() {
          IOException exception = new IOException("Error");
          ...
          Mockito.when(mClient.registerUser(Matchers.anyObject())). thenThrow(exception);
          new RegistrationRequest(mClient).execute(registrationData);
     }
}

In both tests we have isolated registration code and we won’t test server responses or our internet provider. That was our goal and we did it properly. As you can see, there wasn’t a lot of work.

Conclusion

Please write tests. I know that our projects sometimes are very short or deadlines are very tight but thanks to the tools I presented above, everything can go smooth and easy. TDD will help you with that, so please use that process in your development and you will see fewer bugs and regressions in your application.