Monday, September 7, 2015

Writing ViewHolder Matcher with Espresso for Android.

Recently I had a need to adapt my Espresso tests to operate on RecyclerView after migration from ListViews. The current actions that are available for RecyclerView based on item position working fine but I don't like to be dependent on position since data in my tests is created dynamically.

I've googled the ViewHolder matchers and found only this link without any practical examples - RecyclerViewActions.

Then based on already created Matcher<Object> used in onData(...) I've created Matcher<VH> which was not so difficult.

Let's assume each item in RecyclerView adapter has subject, represented by TextView. The below matcher will search for item in RecylerView with unique subject which I provide into matcher. Feel free to use it:

public static Matcher<RecyclerView.ViewHolder> withItemSubjectInViewHolder(final String itemSubject) {
    Checks.checkNotNull(itemSubject);
    return new BoundedMatcher(RecyclerView.ViewHolder, MyListRecyclerViewItemAdapter.MyViewHolder.class) {
        @Override
        public boolean matchesSafely(MyListRecyclerViewItemAdapter.MyViewHolder holder) {
            boolean isMatches = false;

            if (!(holder.subject == null)) {
                isMatches = ((itemSubject.equals(holder.subject.getText().toString()))
                        && (holder.subject.getVisibility() == View.VISIBLE));
            }
            return isMatches;
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("with item subject: " + itemSubject.toString());
        }
    };
}
And the possible usage is:
onView(withId(R.id.adapter_list)).perform(scrollToHolder(withItemSubjectInViewHolder(itemSubject)));
onView(withId(R.id.adapter_list)).perform(actionOnHolderItem(withItemSubjectInViewHolder(itemSubject), click()));