Writing tests is not the most glamorous part of developing an Android application but it is an invaluable one. There are two main types of tests for Android apps - unit tests and instrumentation tests.

Unit tests test small components in isolation while instrumentation tests test larger functionality like whole screens or even multiple screens.

Unit tests

Unit tests are easy to write and fast to execute since they don't need an Android device to run - they run on a plain JVM.

This means that the code needs to be cleanly separated so we can write useful unit tests for it.

Usually it's very useful to test the presenter logic, assuming MVP is used in the app (see MVP chapter about details). This means that presenters need to be Android-agnostic.

In general, the most useful tests cover the business logic of the app and the most important functionalities, paying special attention to edge cases.


The following method tests if the ticket barcode conforms to the specified format.

 * @param code the ticket code from a scanned ticket
 * @return true if the ticket code is valid
public static boolean isTicketCodeValid(@NonNull String code) {
    if (code.length() != TICKET_BARCODE_LENGTH) {
        return false;

    int[] digits = new int[code.length()];

    for (int i = 0, length = code.length(); i < length; i++) {
        if (!Character.isDigit(code.charAt(i))) {
            // all the characters need to be digits
            return false;
        digits[i] = Character.digit(code.charAt(i), 10);

    int controlDigit = 0;
    for (int i = digits.length - 2; i > -1; i = i - 2) {
        controlDigit += digits[i];
    controlDigit = 3 * controlDigit;
    for (int i = digits.length - 3; i > -1; i = i - 2) {
        controlDigit += digits[i];
    controlDigit = 10 - controlDigit % 10;
    controlDigit = controlDigit < 10 ? controlDigit : 0;
    return controlDigit == digits[digits.length - 1];

And these are the tests for the method:

class TicketCodeUtilTest {

    fun shouldRecognizeCorrectTicketCode() {

    fun shouldRecognizeIncorrectTicketCode() {

    fun shouldRecognizeIncorrectTicketCodeIfNotAllDigits() {

    fun shouldRecognizeIncorrectTicketCodeIfLessThan22() {

    fun shouldRecognizeIncorrectTicketCodeIfMoreThan22() {

    fun shouldRecognizeIncorrectTicketCodeIfEmpty() {

Notice that these tests don't just test the happy path, they also test edge cases where the input isn't completely numeric or of the required length.


These libraries make it easier to write unit tests:

As seen in the example above, we like to write tests in Kotlin because it's less verbose than Java and often leads to smaller, more readable tests.

Instrumentation tests

Instrumentation tests cover more functionality and run on a real Android device (or emulator). Although they take more time to write and execute slower, they test whole features of the app and are also quite useful.

These tests can be written manually using the Espresso test framework or recorded using Espresso test recorder.

For these kind of test you usually want to prepare a mock web server so the tests don't depend on the availability and state of a real test/staging API. See below for more info on how to do that.

Common testing techniques

Building complex objects by deserializing json files

When testing some functionality requires you to build a complex object with many nested objects, you can make your life easier by placing a json representation of that object into the test resources and deserializing it at the beginning of the test.

This will decrease the test code and make it more readable.

See the ResourceUtils class below for a useful helper class.

Parametrized tests

If you need to test a functionality for many different input and output pairs, you can write a Parametrized JUnit test.


class FullSerializationTest(val responseFilename: String) {

    companion object {

        @Parameterized.Parameters(name = "{0}")
        fun responseFilenames(): Array<Any> {
            return listOf(
                    .map { "slip-responses/$it.json" }

     * We test if the response we got from the API can be saved to the db and retrieved
     * without losing any information or failing for all the responses we have.
    fun serializationAndDeserialization() {
        val response = ResourceUtils.readFromFile(responseFilename)
        val downloadedGameTicket = SerializationUtil.deserializeGameTicketResponse(response)

        val databaseTicket = SerializationUtil.toDbModel(downloadedGameTicket)
        val databaseGameTicket = SerializationUtil.fromDbModel(databaseTicket)


The above test class will run the same test for each of the 9 input strings.

You may also find the Burst library useful.

Using a mock web server to simulate API

When testing, we don't want to execute real API calls. Instead, we use mock server and specify each response so we can test different use cases. Usually good idea is to provide OkHttp's MockWebServer with Dagger. Then you have to start it and shutdown it before and after each test.

In each test you may want to enqueue response(s). Enqueuing works by FIFO)

To enqueue response, first you have to create one and put it in, let's say /resources/mockdata, folder in androidTest flavor. Then you need to create utils class for reading from resources file. It may look like something like this:

import java.io.InputStream;
import java.util.Scanner;

 * Utility methods for accessing resources bundled with test APK. Standard Android Resources don't seem to work for test APK
 * (unable to fetch R.java).
 * <p>
 * Resources should be placed under /resources/mockdata folder in androidTest flavour. Use {@link #readFromFile(String)} to read a text
 * file to String giving only a name of the file located in /resources/mockdata folder.
public class ResourceUtils {

    private static final String MOCK_DATA_DIRECTORY = "mockdata/%s";

    private ResourceUtils() {

     * Converts InputStream to String.
    public static String convertStreamToString(InputStream is) {
        Scanner s = new Scanner(is, "UTF-8").useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";

     * Reads a resource file to <code>String</code>.
    public static String readFromFile(String filename) {
        InputStream is = ResourceUtils.class.getClassLoader().getResourceAsStream(String.format(MOCK_DATA_DIRECTORY, filename));
        return convertStreamToString(is);

Once you have that, you can simply enqueue your next 200 server response by putting this in your test:

String body = ResourceUtils.readFromFile(filename);
MockResponse mockResponse = new MockResponse().setBody(body).setResponseCode(HttpURLConnection.HTTP_OK);

and next API call will result with that response.

Redirect responses

When you want to test redirection you have to be careful how you set URL in the Location header. When you are creating the URL you must set MockServer's host and port in the URL scheme. The code below is an example of a test that successfully tests redirection.

public void mobileDataUserLowBudget() throws Exception {

    //First response with Location header and 302 code
    MockResponse mockResponse = new MockResponse()
            .setHeader("Location", "http://" + mockWebServer.getHostName() + ":" + mockWebServer.getPort() + "/molimo-vas-dopunite-kredit");

    //Second response with empty body and 200 code

    //Check that request exists and that it is made to proper URL
    RecordedRequest request = takeLastRequest();

To properly test redirection you must: