Espresso Testing On Android

It’s just not possible to test for these conditions using unit testing. We’re going to have to use another testing tool to test our GUIs or activities. And unfortunately it also means we’re back to using devices and emulators to do our testing again.
There are lots of options out there, such as UIAutomator, Calabash, Robotium and Selenium. Until recently I’ve been using Calabash because of its Given/When/Then writing format which works great with business users. However there are significant advantages to using Espresso which are too hard to resist.
All of these other products are third party products whereas Espresso is a Google first party product. Normally this wouldn’t be any sort of advantage but because of Espresso’s ability to hook into the Android lifecycle it does a wonderful job of knowing exactly when the activity is ready to perform your tests. GUI tests in Android are typically full of sleep() commands to ensure that the Activity is ready to accept your data. With Espresso there is simply no need for any waiting or sleeping, it just fires the test when the app is ready to accept the input data. This synchronization between the UI thread and Espresso means that tests run much more reliably than with the other tools. If a test fails then it’s because there’s an error in your code rather than you need to add more time to the sleep command.
onView
While we already looked at the Espresso in in our earlier blog Android Testing it makes sense to go back to basics and do a real Hello World Espresso test.
In the last post we showed how to setup the Espresso environment as follows
- Prerequisites – Intall the Android Support Repository
- Test classes are in the src/androidTest/java folders
- Add Espresso dependency in build.gradle (app) file
- Choose Android Test Instrumentation Test Artifact in Build Variant
- Create GUI tests
- Right click on tests to run tests
Instead of jUnit or Hamcrest assertions Espresso uses the OnView format which uses a ViewMatcher to find the element in the activity we’re testing, ViewAction performs the action e.g. click, and matches is the assertion which makes sure the text matches and the test passes.
1
2
3
|
onView(ViewMatcher) .perform(ViewAction) .check(ViewAssertion); |
Hello World
Listing 1 shows the code for the standard Android Hello World app
1
2
3
4
5
6
7
8
9
10
11
|
public class MainActivity extends Activity { private TextView mLabel; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } |
Listing 1. Hello World
Figure 1 shows the app running on the emulator. Our simple Espresso test is going to find the text and make sure it’s really saying Hello world!

The code for the simple test is in Listing 2. The test is annotated as a @LargeTest
because we need the emulator to run Espresso tests. We’re using a jUnit4 rule to launch the Main Activity, see the @Rule
annotation.
Once we have access to the Activity we use the onView code to find our Hello World text and a .check
to see if the text is what it was defined as in the strings.xml file. In this case there is no need for the perform step so that is omitted.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@RunWith (AndroidJUnit4. class ) @LargeTest public class MainActivityTest { @Rule public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<> (MainActivity. class ); @Test public void helloWorldTest() { onView(withId(R.id.hello_world)) .check(matches(withText(R.string.hello_world))); } } |
Listing 2. Hello World Espresso test
The test passes and the results are shown in Android Studio similar to the unit test output, see Figure 2.

Adding Buttons
Next let’s add a button to our Hello World code. We do this by adding the following code to our activity_main.xml file, see Listing 3. The button_label string will also need to be added to the strings.xml file. Note that the button is enabled by default.
1
2
|
<button android_id= "@+id/button" android:text= "@string/button_label" android:layout_width= "wrap_content" android:layout_height= "wrap_content" > </button> |
Listing 3. Adding Hello World button.
Figure 3 shows our modified app with the new button.

We want to make sure the button is on or enabled. The test code is now shown in Listing 4. This time we’re using the perform action to click the button.
1
2
3
4
5
6
7
8
|
@Test public void helloWorldButtonTest(){ onView(withId(R.id.button)) .perform(click()) .check(matches(isEnabled())); } |
Listing 4. onView button test
The test successfully runs as everything is green, see Figure 4.

ViewMatchers
Table 1 shows the available ViewMatcher options.
Category | Matcher |
---|---|
User Properties | withId, withText, withTagKey, withTagValue, hasContentDescription,withContentDescription, withHint, withSpinnerText, hasLinks, hasEllipsizedText, hasMultilineTest |
UI Properties | isDisplayed, isCompletelyDisplayed, isEnabled, hasFocus, isClickable,isChecked, isNotChecked, withEffectiveVisibility, isSelected |
ObjectMatcher | allOf, anyOf, is, not, endsWith, startsWith, instanceOf |
Hierarchy | withParent, withChild, hasDescendant, isDescendantOfA, hasSibling, isRoot |
Input | supportsInputMethods, hasIMEAction |
Class | isAssignableFrom, withClassName |
Root Matchers | isFocusable, isTouchable, isDialog, withDecorView, isPlatformPopup |
Table 1. ViewMatcher
ViewActions
Table 2 shows the available ViewAction options.
Category | Action |
---|---|
Click/Press | click, doubleClick, longClick, pressBack, pressIMEActionButton, pressKey,pressMenuKey, closeSoftKeyboard, openLink |
Gestures | scrollTo, swipeLeft, swipeRight, swipeUp, swipeDown |
Text | allOf, anyOf, is, not, endsWith, startsWith, instanceOf |
Table 2. ViewAction
ViewAssertions
Table 3 shows the available ViewAssertion options.
Package | Assertions |
---|---|
Layout Assertions | noEllipseizedText, noMultilineButtons, noOverlaps |
Position Assertions | isLeftOf, isRightOf, isLeftAllginedWith, isRightAlignedWith, isAbove, isBelow,isBottomAlignedWith, isTopAlignedWith |
Other | clearText, typeText, typeTextIntoFocusedView, replaceText |
Table 3. ViewAssertion
OnData
onView won’t be able to find the data when we’re using any AdapterViews such as ListView, Gridview or Spinner. For AdapterViews we have to use onData in conjunction with the onView to locate and test the item as follows.
The OnData format is as follows:
1
2
3
4
|
onData(ObjectMatcher) .DataOptions .perform(ViewAction) .check(ViewAssertion) |
The DataOptions available are inAdapterView, atPosition or onChildView.
To Do List
To see how this works lets look at how to test To Do List application which has a ListView adapter, see Figure 5.

Our application uses a ListView Adapter. The code can be found in Listing 5.
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
31
|
public class MainActivity extends Activity { private TextView mtxtSelectedItem; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); mtxtSelectedItem = (TextView) findViewById(R.id.txt_selected_item); String[] todolist = {String[] todolist = { "pick up the kids" , "pay bills" , "do laundry" , "buy groceries " , "go the gym" , "clean room" , "call mum" }; List<String> list = Arrays.asList(todolist); ArrayAdapter<String> adapter = new ArrayAdapter( this , android.R.layout.simple_list_item_1, list); ListView listView = (ListView) findViewById(R.id.list_of_todos); listView.setAdapter(adapter); listView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String text = ((TextView) view).getText().toString(); Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show(); mtxtSelectedItem.setText(text); } }); } } |
Listing 5. To Do List code
A simple test to make sure everything is working ok would be to pick something on the To Do List, such as “go to the gym”. The code to is shown in Listing 6. We’re telling the test to look at position [4] in the AdapterView in the onData code and then passing that to the onView so that it can check that the text does indeed say “go to the gym”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@RunWith (AndroidJUnit4. class ) @LargeTest public class MainActivityTest { @Rule public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity. class ); @Test public void toDoListTest(){ onData(anything()) .inAdapterView(withId(R.id.list_of_todos)).atPosition( 4 ) .perform(click()); onView(withId(R.id.txt_selected_item)) .check(matches(withText( "go to the gym" ))); } } |
Listing 6. onData test code.
Run the test once again using the emulator or on a device.
Espresso Tests in Jenkins
To run the Espresso tests in Jenkins, click on Add Build Step->Invoke Gradle Script and add the connectedCheck task, see figure 6.

Espresso needs an emulator to perform its tests, so you also need to install the Android Emulator Plugin. You can choose to let Jenkins use an existing emulator or create a new one, see Figure 7.

Summary
In this blog we’ve looked at a number of Espresso tests using both onView and onData. Finally if you’re wondering how many Espresso tests we should have in our suite of tests, then go back to our Agile testing pyramid in our earlier blog Android Testing and you should see we should always have a lot more unit tests than Espresso tests.
Note a complete set of these testing blogs have been compiled together into an Agile Android mini-book, published by Apress and available from Amazon
This is Part 4 of 6, the remaining blogs can be found below.
Part 1 – Agile Testing on Android
Part 2 – Android Unit Testing
Part 3 – Hamcrest, JaCoCo, Mockito, and Jenkins for Android
Part 4 – Espresso Testing on Android
Part 5 – Android Mocking
Part 6 – Maintaining Someone Else’s Android Code
Read More:
Android Obfuscation Tools Comparison
As you may or may not know, Android apps can be decompiled back…
Hamcrest, JaCoCo, Mockito & Jenkins For Android
JUnit on its own may be all you need but there are a…