Now that we're done with the basics and you finally get the hang of the
allOf() function by using multiple
ViewMatchers we can continue the journey of fixing a different case that leads to
AmbiguousViewMatcherException. Consider the following UI layout where you need to click the check box beside the text "Tokyo Imperial Palace". The problem with this one is that, using multiple ViewMatchers to filter out unique attributes will not work because the checkboxes have identical attributes.
Test engineers who's been using selenium or other UI automation tool may solve this problem by getting all the checkboxes on the screen and then clicking the checkbox that you want by index. It is a valid solution and something that you can do with the help of
UIAutomator but the problem with this solution is that it won't work if the content is dynamic. That means that when a specific content has moved it's position the test will not work as intended.
One approach that I highly recommend is to use the View Hierarchy of the current screen and use the parent, descendant and sibling layout with respect to the UI element that you want to take action to. If you've been exploring at the
ViewMatchers documentation, you've probably stumbled the following functions.
ViewMatchers are different than the
ViewMatchers that we've been discussing in the past two (1, 2) articles because it doesn't match by attribute. Instead it accepts another
ViewMatcher to look if such attributes are present on it's descendant, sibling or parent container.
Before we go to the solution I need to give a brief example on how View Hierarchies work. UI elements in an android app are arranged with the help of layouts. A good analogy is that it's similar to a website's nested
html tags with different
ids. What you see above is an example of how the checkboxes are placed in the app. The
TravelContainerLayout is the the Parent of
EntryContainerLayout. While the
TravelText are the descendants of the
Now that we know how View Hierarchies and Android Layouts work let's go to the code on how we can click the checkbox of "Tokyo Imperial Palace". In narrative format, the quick solution is just easy. Find a check box that has a sibling with a text of "Tokyo Imperial Palace".
Converting it to code will look something like this
Espresso.onView( allOf( withId(R.id.TravelCheckbox), hasSibling( withText("Tokyo Imperial Palace") ) ) ).perform(click())
Always remember that
onView() will always accepts a single argument of type
Matcher. If you need to add additional criteria to narrow the search use the
hasSibling() also accepts a single parameter of type
Matcher and that also applies to
withParent(). And if you need to use multiple attributes to look for an element inside a descendant or sibling, remember that
You can pass
allOf()if you need to use multiple ViewMatchers inside
Necessary for Mastery
The more complicated the layout of the android app you are testing, the more you need to use
withParent(). I'll admit that it's kinda complex and very hard to read especially when the Application under test has deep nesting of layouts but I think that Google's Espresso, at the time of this writing, is the only reliable library that does it's job well for automating ui elements with identical attributes.