How to test Android Apps: Instrumentation Testing

Although you should avoid having your code tied to Android components, it's not always possible. Maybe it's Activity code, or a component that needs Context. But you still should test it.

Those tests will have to run on an emulator or a real device. They are going to be slower. But they are important nevertheless.

Here's how you can do that:

Testing with application context

Sometimes you just need Context to test your code, and not a full activity. For example, testing a database integration, or a file resource loader. For those cases, you can use the ApplicationTestCase.

An example:

public class DatabaseTest extends ApplicationTestCase<MyApplication> {

    private Database database;

    public DatabaseTest() {
        super(MyApplication.class);
    }

    @Override
    public void setUp() throws Exception {
        createApplication();
        database = new Database(getContext());
    }

    public void testSaveCard() throws Exception {
        assertEquals(database.countCards(), 0);
        Card card = new Card("1234");
        database.saveCard(card);
        assertEquals(database.countCards(), 1);
    }

    @Override
    public void tearDown() throws Exception {
        database.deleteAllCards();
    }

}

In this example, we're testing a Database class that needs a Context. After creating the application, we can use the getContext() method to get the application Context and pass it on as an argument. The rest of the code is just a general example.

Testing activities

If you want to test an activity, or a specific view, you need to launch an Activity. These tests are even slower than Application tests. But it's important to have these instrumentation tests, that run like an user interacting with your app.

Instrumentation tests are tedious to write, and often run into race conditions. To ease the load, you should use a UI testing library to help you. The most popular right now is Espresso, from the Android Testing Support Library. It helps you find views, perform actions, wait for the consequences and check state.

Here's an example of an Activity instrumentation test.

@RunWith(AndroidJUnit4.class)
public class CalculatorActivityTest {

    @Rule
    public ActivityTestRule<CalculatorActivity> activityTestRule =
        new ActivityTestRule<>(CalculatorActivity.class);

    @Test
    public void testSum() throws Exception {
        onView(withId(R.id.calculator_input))
            .perform(typeText("2 + 2"));
        onView(withText("="))
            .perform(click());
        onView(withId(R.id.calculator_result))
            .check(matches(withText("4")));
    }

}

The example tests a calculator, with an input EditText, a button with the = caption and n result TextView. Espresso can find views by id, text, content description, hint, view hierarchy and other ViewMatchers. It can perform actions like clicking, swiping, general navigation and other ViewActions. Finally, Espresso can assert state using the same ViewMatchers, or other ViewAssertions.

Waiting

Espresso already does a good job knowing when to wait before assertions. But if you have custom animations, or another special conditions to wait for, you will need to add them.

Espresso comes with IdlingResource for that, but I've found it a bit too complex for most cases. I ended up implementing a simple Wait helper. Here's how you can use it:

new Wait(new Wait.Condition() {
    @Override
    public boolean check() {
        return someView.isEnabled();
    }
}).waitForIt();

Resources

On the next and final article, called Other details, we'll discuss additional details, such as mocking components and servers, generating fake data and tracking code test coverage.


How to test Android Apps

  1. Introduction
  2. Architecture
  3. Unit Testing
  4. Instrumentation Testing
  5. Other details
TechSérgiotesting, espresso