Skip to content

Most visited

Recently visited

navigation

Espresso basics

This document explains how to complete common automated testing tasks using the Espresso API.

The Espresso API encourages test authors to think in terms of what a user might do while interacting with the application - locating UI elements and interacting with them. At the same time, the framework prevents direct access to activities and views of the application because holding on to these objects and operating on them off the UI thread is a major source of test flakiness. Thus, you will not see methods like getView() and getCurrentActivity() in the Espresso API. You can still safely operate on views by implementing your own subclasses of ViewAction and ViewAssertion.

API components

The main components of Espresso include the following:

Example:

onView(withId(R.id.my_view))        // withId(R.id.my_view) is a ViewMatcher
    .perform(click())               // click() is a ViewAction
    .check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion

Finding a view

In the vast majority of cases, the onView() method takes a hamcrest matcher that is expected to match one — and only one — view within the current view hierarchy. Matchers are powerful and will be familiar to those who have used them with Mockito or JUnit. If you are not familiar with hamcrest matchers, we suggest you start with a quick look at this presentation.

Often the desired view has a unique R.id and a simple withId matcher will narrow down the view search. However, there are many legitimate cases when you cannot determine R.id at test development time. For example, the specific view may not have an R.id or the R.id is not unique. This can make normal instrumentation tests brittle and complicated to write because the normal way to access the view—with findViewById()— does not work. Thus, you may need to access private members of the Activity or Fragment holding the view or find a container with a known R.id and navigate to its content for the particular view.

Espresso handles this problem cleanly by allowing you to narrow down the view using either existing ViewMatcher objects or your own custom ones.

Finding a view by its R.id is as simple as calling onView():

onView(withId(R.id.my_view))

Sometimes, R.id values are shared between multiple views. When this happens an attempt to use a particular R.id gives you an exception, such as AmbiguousViewMatcherException. The exception message provides you with a text representation of the current view hierarchy, which you can search for and find the views that match the non-unique R.id:

java.lang.RuntimeException:
com.google.android.apps.common.testing.ui.espresso.AmbiguousViewMatcherException
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

...

+----->SomeView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=false, enabled=true,
selected=false, is-layout-requested=false, text=,
root-is-layout-requested=false, x=0.0, y=625.0, child-count=1}
****MATCHES****
|
+------>OtherView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=true, enabled=true,
selected=false, is-layout-requested=false, text=Hello!,
root-is-layout-requested=false, x=0.0, y=0.0, child-count=1}
****MATCHES****

Looking through the various attributes of the views, you may find uniquely identifiable properties. In the example above, one of the views has the text "Hello!". You can use this to narrow down your search by using combination matchers:

onView(allOf(withId(R.id.my_view), withText("Hello!")))

You can also choose not to reverse any of the matchers:

onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))

See ViewMatchers for the view matchers provided by Espresso.

Considerations

Performing an action on a view

When you have found a suitable matcher for the target view, it is possible to perform instances of ViewAction on it using the perform method.

For example, to click on the view:

onView(...).perform(click());

You can execute more than one action with one perform call:

onView(...).perform(typeText("Hello"), click());

If the view you are working with is located inside a ScrollView (vertical or horizontal), consider preceding actions that require the view to be displayed—such as click() and typeText()—with scrollTo(). This ensures that the view is displayed before proceeding to the other action:

onView(...).perform(scrollTo(), click());

See ViewActions for the view actions provided by Espresso.

Checking view assertions

Assertions can be applied to the currently selected view with the check() method. The most used assertion is the matches() assertion. It uses a ViewMatcher object to assert the state of the currently selected view.

For example, to check that a view has the text "Hello!":

onView(...).check(matches(withText("Hello!")));

If you want to assert that "Hello!" is content of the view, the following is considered bad practice:

// Don't use assertions like withText inside onView.
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));

On the other hand, if you want to assert that a view with the text "Hello!" is visible—for example after a change of the views visibility flag—the code is fine.

View assertion simple test

In this example, SimpleActivity contains a Button and a TextView. When the button is clicked, the content of the TextView changes to "Hello Espresso!".

Here’s how to test this with Espresso:

Click on the button

The first step is to look for a property that helps to find the button. The button in the SimpleActivity has a unique R.id, as expected.

onView(withId(R.id.button_simple))

Now to perform the click:

onView(withId(R.id.button_simple)).perform(click());

Verify the TextView text

The TextView with the text to verify has a unique R.id too:

onView(withId(R.id.text_simple))

Now to verify the content text:

onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));

Checking data loading in adapter views

AdapterView is a special type of widget that loads its data dynamically from an Adapter. The most common example of an AdapterView is ListView. As opposed to static widgets like LinearLayout, only a subset of the AdapterView children may be loaded into the current view hierarchy. A simple onView() search would not find views that are not currently loaded.

Espresso handles this by providing a separate onData() entry point which is able to first load the adapter item in question, bringing it into focus prior to operating on it or any of its children.

Warning: Custom implementations of AdapterView can have problems with the onData() method if they break inheritance contracts, particularly the getItem() API. In such cases, the best course of action is to refactor your application code. If you cannot do so, you can implement a matching custom AdapterViewProtocol. For more information, take a look at the default AdapterViewProtocols class provided by Espresso.

Adapter view simple test

This simple test demonstrates how to use onData(). SimpleActivity contains a Spinner with a few items that represent types of coffee beverages. When an item is selected, there is a TextView that changes to "One %s a day!", where %s represents the selected item.

The goal of this test is to open the Spinner, select a specific item, and verify that the TextView contains the item. As the Spinner class is based onAdapterView, it is recommended to use onData() instead of onView() for matching the item.

Open the item selection

onView(withId(R.id.spinner_simple)).perform(click());

Select an item

For the item selection, the Spinner creates a ListView with its contents. This view can be very long, and the element might not be contributed to the view hierarchy. By using onData() we force our desired element into the view hierarchy. The items in the Spinner are strings, so we want to match an item that is equal to the String "Americano":

onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());

Verify text is correct

onView(withId(R.id.spinnertext_simple))
    .check(matches(withText(containsString("Americano"))));

Debugging

Espresso provides useful debugging information when a test fails:

Logging

Espresso logs all view actions to logcat. For example:

ViewInteraction: Performing 'single click' action on view with text: Espresso

View hierarchy

Espresso prints the view hierarchy in the exception message when onView() fails.

java.lang.RuntimeException:
com.google.android.apps.common.testing.ui.espresso.AmbiguousViewMatcherException
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

...

+----->SomeView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=false, enabled=true,
selected=false, is-layout-requested=false, text=,
root-is-layout-requested=false, x=0.0, y=625.0, child-count=1}
****MATCHES****
|
+------>OtherView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=true, enabled=true,
selected=false, is-layout-requested=false, text=Hello!,
root-is-layout-requested=false, x=0.0, y=0.0, child-count=1}
****MATCHES****

When dealing with a complicated view hierarchy or unexpected behavior of widgets it is always helpful to use the Hierarchy Viewer in Android Studio for an explanation.

Adapter view warnings

Espresso warns users about presence of AdapterView widgets. When an onView() operation throws a NoMatchingViewException and AdapterView widgets are present in the view hierarchy, the most common solution is to use onData(). The exception message will include a warning with a list of the adapter views. You may use this information to invoke onData() to load the target view.

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)