Friday, May 8, 2015

Espresso: Match Toolbar Title

How do check the value of the title bar in Espresso?

Let's take a look with Hierarchy Viewer. Remember to run it with ANDROID_HVPROTO=ddm if your device is not rooted.

First attempt: Match the TextView

We found our TextView in the hierarchy, but it does not have an id. However, we notice that it is a child of the Toolbar, so we can match it using withParent.

@Test
public void toolbarTitle() {
  CharSequence title = InstrumentationRegistry.getTargetContext()
    .getString(R.string.my_title);
  matchToolbarTitle(title);
}

private static ViewInteraction matchToolbarTitle(
    CharSequence title) {
  return onView(
      allOf(
          isAssignableFrom(TextView.class),
          withParent(isAssignableFrom(Toolbar.class))))
      .check(matches(withText(title.toString())));
}

This is how we look for the view to verify the title:

  • It is a TextView
  • It has a parent that is a Toolbar

This works, but it depends on the inner structure of Toolbar, which is not a part of the public API and may change in future versions. Let's make it better.

Second attempt: Custom matcher with Toolbar.getTitle()

private static ViewInteraction matchToolbarTitle(
    CharSequence title) {
  return onView(isAssignableFrom(Toolbar.class))
      .check(matches(withToolbarTitle(is(title))));
}

private static Matcher<Object> withToolbarTitle(
    final Matcher<CharSequence> textMatcher) {
  return new BoundedMatcher<Object, Toolbar>(Toolbar.class) {
    @Override public boolean matchesSafely(Toolbar toolbar) {
      return textMatcher.matches(toolbar.getTitle());
    }
    @Override public void describeTo(Description description) {
      description.appendText("with toolbar title: ");
      textMatcher.describeTo(description);
    }
  };
}

withToolbarTitle() returns a custom BoundedMatcher, which gives us type safety. In matchesSafely(), we call Toolbar.getTitle() to check its value.

To use this custom matcher, we change our helper function to look for the Toolbar itself, and check that it has the expected title. Notice that we pass is(title) to withToolbarTitle, which takes a text matcher rather than a string. That way we can use other matchers such as startsWith and endsWith.

Source code and notes

https://github.com/chiuki/espresso-samples/ under toolbar-title

  • The Toolbar is actually android.support.v7.widget.Toolbar because we are using AppCompat in the sample.
  • In both attempts, we assume that there is only one Toolbar. If you have more than one, you will need to have some extra matchers to pinpoint the one you want to match.
  • We use a helper function matchToolbarTitle() to hide the implementation details of how we match the toolbar title. That way, if the underlying code changes, we only need to update one place for all our tests that wants to match the toolbar title.
  • The helper function matchToolbarTitle() returns a ViewInteraction. This allows us to chain this call to verify other properties of the Toolbar. i.e. you can do something like this:
    matchToolbarTitle(title)
      .check(matches(isDisplayed()));
    

Article inspired by discussion with Danny Roa, Jacob Tabak and Jake Wharton.

Like this article? Take a look at the outline of my Espresso book and fill in this form to push me to write it! Also check out the published courses: https://gumroad.com/chiuki

1 comment:

Inline coding questions will not be answsered. Instead, ask on StackOverflow and put the link in the comment.

  1. Hi its old post but thanks second attempt work good ;)

    ReplyDelete