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.

  • withParent()
  • hasDescendant()
  • hasSibling()

The above 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 class and ids. What you see above is an example of how the checkboxes are placed in the app. The TravelContainerLayout is the the Parent of HeaderLayout and EntryContainerLayout. While the TravelCheckbox and TravelText are the descendants of the EntryContainerLayout.

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 allOf() function. ViewMatchers like hasSibling() also accepts a single parameter of type Matcher and that also applies to hasDescendant() and 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 hasDescendant(), withParent() and hasSibling

Necessary for Mastery

The more complicated the layout of the android app you are testing, the more you need to use hasSibling(), hasDescendant() and 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.