Espresso for Android – easy access to adapters for onData

How to write a test case, where on a landscape screen you have two fragments, left and right and both are “ListFragment” descendants which by the fate of the Android UI can both have the id “android.R.id.list”.

First, modify your fragments to set a TAG on each ListView.

E.g. in the onResume of the fragment.

public static final String LIST_TAG = "SETTING_GROUP_LIST";
 
public void onResume() {
  super.onResume();
  if (getListView() != null) {
    getListView().setTag(LIST_TAG);
  }
}

Then, in your test, create some matchers and helpers. I do these in a class called “CustomMatchers”

public class CustomMatchers {
 
  public static String upCase(String in){
    return in.substring(0,1).toUpperCase() + in.substring(1);
  }
 
  public  static DataInteraction onSettingRow(String str) {
    DataInteraction interaction = onData(allOf(
        is(instanceOf(SettingItem.class)),
        withSettingItemContent(upCase(str))));
    return interaction
        .inAdapterView(withTagValue(withStringMatching(SettingGroupListFragment.LIST_TAG)));
  }
 
  @SuppressWarnings("rawtypes")
  public static  static Matcher<Object> withSettingItemContent(final Matcher<string> itemTextMatcher) {
    checkNotNull(itemTextMatcher);
    return new BoundedMatcher<Object, SettingItem>(SettingItem.class) {
      @Override
      public boolean matchesSafely(SettingItem settingItem) {
        return itemTextMatcher.matches(settingItem.title);
      }
 
      @Override
      public void describeTo(Description description) {
        description.appendText("with SettingItem title: ");
        itemTextMatcher.describeTo(description);
      }
    };
  }
 
  public static Matcher<object> withStringMatching(String expectedText) {
    checkNotNull(expectedText);
    return withStringMatching(equalTo(expectedText));
  }
 
 
  @SuppressWarnings("rawtypes")
  public static Matcher<object> withStringMatching(final Matcher<string> itemTextMatcher) 
  {
    checkNotNull(itemTextMatcher);
    return new BoundedMatcher<Object, String>(String.class) {
      @Override
      public boolean matchesSafely(String string) {
        return itemTextMatcher.matches(string);
      }
 
      @Override
      public void describeTo(Description description) {
        description.appendText("with string: ");
        itemTextMatcher.describeTo(description);
      }
    };
  }
 
}

The following things are important to create – a “withStringMatching” matcher, this seems missing from espresso. This has to be created as you are matching a call to “getTag” with a string. Then you need to create matchers to find the rows in your adapter. In this example, I store “SettingItem” and match on the “title” of the “SettingItem”. Last and most important, is to create a static method that returns DataInteraction. In this example, it is called “onSettingRow” which I pass the “SettingItem” title I am looking for. The parts you will customize are highlighted below, they are the method name, the item matcher and the tag used to identify the ListView. Create as many of these functions as you need.

public  static DataInteraction onSettingRow(String str) {
    DataInteraction interaction = onData(allOf(
        is(instanceOf(SettingItem.class)),
        withSettingItemContent(upCase(str))));
    return interaction
        .inAdapterView(withTagValue(withStringMatching(SettingGroupListFragment.LIST_TAG)));
  }

Then in your test, using it is quite simple…

onSettingRow(SettingsConstants.PREF_GENERAL).check(matches(isCompletelyDisplayed()));
 
onSettingRow(SettingsConstants.PREF_GENERAL).perform(click());

That’s all there is to it… Check out the GitHub gist for almost complete files…

Leave a Reply

Your email address will not be published. Required fields are marked *