- Matching a view next to another view
- Matching a view that is inside an ActionBar
- Asserting that a view is not displayed
- Asserting that a view is not present
- Asserting that a data item is not in an adapter
- Using a custom failure handler
- Using inRoot to target non-default windows
- Matching a view that is a footer/header in a ListView
Matching a view next to another view
A layout could contain certain views that are not unique by themselves (e.g. a repeating call button in a table of contacts could have the same R.id, contain the same text and have the same properties as other call buttons within the view hierarchy).
For example, in this activity, the view with text “7” repeats across multiple rows:
Often, the non-unique view will be paired with some unique label that’s located next to it (e.g. a name of the contact next to the call button). In this case, you can use the hasSibling matcher to narrow down your selection:
Matching a view that is inside an ActionBar
The ActionBarTestActivity has two different action bars: a normal ActionBar and a contextual action bar that is created from a options menu. Both action bars have one item that is always visible and two items that are only visible in overflow menu. When an item is clicked, it changes a TextView to the content of the clicked item.
Matching visible icons on both of the action bars is easy:
The code looks identical for the contextual action bar:
Clicking on items in the overflow menu is a bit trickier for the normal action bar as some devices have a hardware overflow menu button (they will open the overflowing items in an options menu) and some devices have a software overflow menu button (they will open a normal overflow menu). Luckily, Espresso handles that for us.
For the normal action bar:
This is how this looks on devices with a hardware overflow menu button:
For the contextual action bar it is really easy again:
See the full code for these samples: ActionBarTest.java.
Asserting that a view is not displayed
After performing a series of actions, you will certainly want to assert the state of the UI under test. Sometimes, this may be a negative case (for example, something is not happening). Keep in mind that you can turn any hamcrest view matcher into a ViewAssertion by using ViewAssertions.matches.
In the example below, we take the isDisplayed matcher and reverse it using the standard “not” matcher:
The above approach works if the view is still part of the hierarchy. If it is not, you will get a
NoMatchingViewException and you need to use
ViewAssertions.doesNotExist (see below).
Asserting that a view is not present
If the view is gone from the view hierarchy (e.g. this may happen if an action caused a transition to another activity), you should use
Asserting that a data item is not in an adapter
To prove a particular data item is not within an AdapterView you have to do things a little differently. We have to find the AdapterView we’re interested in and interrogate the data its holding. We don’t need to use onData(). Instead, we use onView to find the AdapterView and then use another matcher to work on the data inside the view.
First the matcher:
Then the all we need is an onView that finds the AdapterView:
And we have an assertion that will fail if an item that is equal to “item: 168” exists in an adapter view with the id list.
For the full sample look at AdapterViewTest#testDataItemNotInAdapter.
Using a custom failure handler
Replacing the default FailureHandler of Espresso with a custom one allows for additional (or different) error handling - e.g. taking a screenshot or dumping extra debug information.
The CustomFailureHandlerTest example demonstrates how to implement a custom failure handler:
This failure handler throws a MySpecialException instead of a NoMatchingViewException and delegates all other failures to the DefaultFailureHandler. The CustomFailureHandler can be registered with Espresso in the setUp() of the test:
Using inRoot to target non-default windows
Surprising, but true - Android supports multiple windows. Normally, this is transparent (pun intended) to the users and the app developer, yet in certain cases multiple windows are visible (e.g. an auto-complete window gets drawn over the main application window in the search widget). To simplify your life, by default Espresso uses a heuristic to guess which Window you intend to interact with. This heuristic is almost always “good enough”; however, in rare cases, you’ll need to specify which window an interaction should target. You can do this by providing your own root window (aka Root) matcher:
Matching a view that is a footer/header in a ListView
Headers and footers are added to ListViews via the
addFooterView() APIs. To ensure
Espresso.onData() knows what data object to match, make sure to pass a preset data object value as the second parameter to
addFooterView(). For example:
Then, you can write a matcher for the footer:
And loading the view in a test is trivial:
Take a look at the full code sample at: AdapterViewtest#testClickFooter