Skip to main content

Discovering Espresso for Android: creating custom Matchers.

Hi! This time I'll talk about custom Matchers that can be used with Espresso for Android (and not only). We'll go through steps how to create them and I'll provide you examples of already existing and very useful ones.

First of all a couple of words about Hamcrest library, which provides us with common matchers and possibility to create custom matchers, to pay tribute to it's authors.

From Humcrest project main page - Hamcrest provides a library of matcher objects (also known as constraints or predicates) allowing 'match' rules to be defined declaratively, to be used in other frameworks. Typical scenarios include testing frameworks, mocking libraries and UI validation rules.

In one of my previous post I've described already how to use some custom Hamcrest matchers. The base idea is that matcher is initialized with the expected values, which are compared against the actual object we are matching when invoking it.

Among of the common matchers you can create your custom matchers using abstract class TypeSaveMatcher.class , with three methods to override:

public boolean matchesSafely(Fruit fruit) - matcher's logic,
public void describeTo(Description description) - description of the expected result,
protected void describeMismatchSafely(Fruit item, Description mismatchDescription) - description of actual value.

Example:
public static Matcher<Object> withItemText(String itemText) {
  // use preconditions to fail fast when a test is creating an invalid matcher.
  checkArgument(!(itemText.equals(null)));
  return withItemText(equalTo(itemText));
}

public static Matcher<Object> withItemText(final Matcher<String> matcherText) { 
  // use preconditions to fail fast when a test is creating an invalid matcher.
  checkNotNull(matcherText);
  return new TypeSafeMatcher<Object>() {
    
    @Override
    public void describeTo(Description description) {
      description.appendText("expected text: " + matcherText);
    }

    @Override
    public void describeMismatchSafely(Object item description, Description mismatchDescription) {
      mismatchDescription.appendText("actual text: " + item.toString());
    }

    @Override
    public boolean matchesSafely(Object item) {
      return matcherText.equals(item);
    }
  };
} 
Here TypeSafeMatcher does the cast to a Object for us. The matchesSafely() method checks if the Object contains a text equal to itemText - and the describeTo() method produces a failure message when a test fails. Sometimes describeMismatchSafely() method is omitted.

Usage:
import static com.your.package.test.Matchers.withItemText;
... 
onData(withItemText("someText")).inAdapterView(withId(R.id.someId)).perform(click()); 
Esspresso introduced one more matcher type - BoundedMatcher<T,S extends T> - some matcher sugar that lets you create a matcher for a given type but only process items of a specific subtype of that matcher.

Example:
//Matcher for checking if EditText fields contain text hints
public static Matcher<View> withItemHint(String itemHintText) {
  // use preconditions to fail fast when a test is creating an invalid matcher.
  checkArgument(!(itemHintText.equals(null)));
  return withItemHint(is(itemHintText));
}

public static Matcher<View> withItemHint(final Matcher<String> matcherText) {
  // use preconditions to fail fast when a test is creating an invalid matcher.
  checkNotNull(matcherText);
  return new BoundedMatcher<View, EditText>(EditText.class) {

    @Override
    public void describeTo(Description description) {
      description.appendText("with item hint: " + matcherText);
    }

    @Override
    protected boolean matchesSafely(EditText editTextField) {
      return matcherText.matches(editTextField.getHint().toString());
    }
  };
}

Usage:
import static com.your.package.test.Matchers.withItemHint;
... 
onView(withItemHint(editText.getHint().toString())).check(matches(isDisplayed())); 
How does it works - we go through all elements on the screen which are the subtypes of View.class, casting them to EditText.class, getting hint text with editTextField.getHint().toString() and matching to itemHintText.

And at the end some useful links:
https://github.com/yandex-qatools/matchers-java - Java library which implements hamcrest matchers for collections and webdriver webelements.
https://gist.github.com/frankiesardo/7490059 - custom matchers from Frankie Sardo for matching drawables.


Popular posts from this blog

Espresso & UIAutomator - the perfect tandem

Espresso for Android is perfect and fast test automation framework, but it has one important limitation - you are allowed to operate only inside your app under test context.

That means that it is not possible to automate tests for such app features like:

application push notificationscontact synchronizationnavigating from another app to your app under test,
since you have to deal with other apps from the mobile device - Notification Bar, Contacts or People app, etc. 
In fact it wasn't possible until the release of UIAutomator 2.0. As stated in Android Developers blog post - "...Most importantly, UI Automator is now based on Android Instrumentation...".  And because of that we can run UIAutomator tests as well as Espresso tests using Instrumentation test runner.

In addition to that we can combine UIAutomator tests together with Espresso tests and this gives us the real power and control over the phone and application under test.

In the below example I'll explain  how …

Discovering Espresso for Android: matching and asserting view with text.

After more than a month of using great test tool from Google - Espresso for Android, I'd like to share with you some of my experience. I assume that you've already added espresso jar into your project, spent some time playing with Espresso samples and have basic understanding how this tool works.

In this post I'll show how to match particular view with text or assert that it contains (or not) specified Strings. Before we start, you have to take a look at Hamcrest matchers - Hamcrest tutorial and API Reference Documentation, which are used together with Espresso's ViewAssertions and ViewMatchers and included into Espresso standalone library. Pay more attention to Matcher<java.lang.String> matchers.

So, here we go. For simplicity following String "XXYYZZ" will be used as a expected text pattern.
Espresso ViewMatchers class implements two String matcher methods withText() and withContentDescription() which will match a view which text is equal to specified…

Discovering Espresso for Android: swiping.

Hi, for today I have some tips about swiping ViewActions in Espresso for Android.

As you may know the latest Espresso release contains new swipeLeft and swipeRight ViewActions. They both are really useful when you'd like to swipe between activity fragments, tab layouts or any other UI elements.

You can use it as any other view action:
onView(withId(R.id.viewId)).perform(swipeRight());
But be aware that doing this you will operate on a view, in our case R.id.viewId, but not on the screen size. That means that to swipe right or left, for example between fragments you have to deal with some parent layout or maybe list view.

If you take a look inside Espresso's ViewActions.java class you will see below code for swipeRight() method:
  public static ViewAction swipeRight() {     return new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT,         GeneralLocation.CENTER_RIGHT, Press.FINGER);   }
As you may guess GeneralLocation.CENTER_LEFT and GeneralLocation.CENTER_RIGHT…