I asked around my Android developers which was the best framework out there for Android automation and the most convincing answer was Espresso, so I decided to give it a try. Espresso is designed to be used in environments where the developers write their own tests (which I think should be everywhere), and promises a concise and beautiful syntax.

Set up

To use espresso you have to set up a test project. I decided I was going to place all my automation tests under tests/automation so I created that folder, moved into it and ran this command:

1
2
3
4
android create test-project \
-n MyProjectAutomation \
-p . \
-m ../../

You can make sure that things work fine by doing:

1
2
3
ant debug
adb -s emulator-5554 install -r bin/MyProjectAutomation-debug.apk
ant test

You will also need to download the jar. Get the latest version’s zip file. Then find the correct jar under bin/espresso-standalone/ and place it inside your libs folder. For this to work you need to tell ant where to find libraries by adding this line to ant.properties:

1
jar.libs.dir=libs

You need to open the AndroidManifest of your test project(The one in tests/automation), remove the currently defined instrumentation and add this one:

1
2
3
<instrumentation
    android:name="com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner"
    android:targetPackage="com.yourapp.something" />

If you try to run your tests again using the steps above you will probably get this:

1
2
3
4
5
6
7
8
9
10
11
12
test:
     [echo] Running tests ...
     [exec] INSTRUMENTATION_STATUS: id=ActivityManagerService
     [exec] INSTRUMENTATION_STATUS: Error=Unable to find instrumentation info for: ComponentInfo{com.myapp.tests/android.test.InstrumentationTestRunner}
     [exec] INSTRUMENTATION_STATUS_CODE: -1
     [exec] android.util.AndroidException: INSTRUMENTATION_FAILED: com.myapp.tests/android.test.InstrumentationTestRunner
     [exec]     at com.android.commands.am.Am.runInstrument(Am.java:676)
     [exec]     at com.android.commands.am.Am.run(Am.java:119)
     [exec]     at com.android.commands.am.Am.main(Am.java:82)
     [exec]     at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
     [exec]     at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:235)
     [exec]     at dalvik.system.NativeStart.main(Native Method)

This happens because under the hood ant test runs this command:

1
adb shell am instrument -w -e coverage false com.myapp.tests/android.test.InstrumentationTestRunner

Since we are going to use Espresso instrumentation we need to fix this so it uses the correct one:

1
adb shell am instrument -w -e coverage false com.myapp.tests/com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner

You probably don’t want to have to type this command every time so we can redefine ant test in our build.xml file like this (Look at the comments for a better way to do this):

1
2
3
4
5
<target name="test" description="Run automation tests with espresso">
    <exec executable="${sdk.dir}/platform-tools/adb" failonerror="true">
        <arg line="shell am instrument -w -e coverage false com.myapp.tests/com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner" />
    </exec>
</target>

Writing tests

All testing frameworks say that it is really easy to write tests if you use them, so I was a little skeptic when I read the same statement for espresso. I was ready for a lot of pain writing my first test but I was surprised by how easy it was.

I created a simple test that will click a button and assert that something is found on the screen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.myapp;

import android.test.ActivityInstrumentationTestCase2;
import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;
import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.matches;
import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withText;

public class MyAppTest extends ActivityInstrumentationTestCase2<MyApp> {
    public MyAppTest() {
        super("com.myapp", MyApp.class);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        getActivity(); // We need to launch our activity
    }

    public void testSomething() {
        // Click on a button
        onView(withId(R.id.a_button))
                .perform(click());

        // Verify that an element is found
        onView(withId(R.id.an_element))
                .check(matches(withText("Some text")));
    }
}

And you can use this command to build, install and run:

1
ant debug install test

The test runs pretty smoothly, so from now on, I’ll be writing tests with my features.

[ android  automation  java  mobile  productivity  testing  ]
Monitoring Kubernetes Resources with Fabric8 Informers
Managing Kubernetes Objects With Yaml Configurations
Introduction to Bazel
Monetizing a Jekyll blog with Adsense
Introduction to Simple Workflow Service (SWF)