How to assert inside a RecyclerView in Espresso?

juhlila picture juhlila · Jul 14, 2015 · Viewed 41.6k times · Source

I am using espresso-contrib to perform actions on a RecyclerView, and it works as it should, ex:

onView(withId(R.id.recycler_view))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click())); //click on first item

and I need to perform assertions on it. Something like this:

onView(withId(R.id.recycler_view))
    .perform(RecyclerViewActions.actionOnItemAtPosition(0, check(matches(withText("Test Text"))));

but, because RecyclerViewActions is, of course, expecting an action, it says wrong 2nd argument type. There's no RecyclerViewAssertions on espresso-contrib.

Is there any way to perform assertions on a recycler view?

Answer

riwnodennyk picture riwnodennyk · Jan 14, 2016

Pretty easy. No extra library is needed. Do:

    onView(withId(R.id.recycler_view))
            .check(matches(atPosition(0, withText("Test Text"))));

if your ViewHolder uses ViewGroup, wrap withText() with a hasDescendant() like:

onView(withId(R.id.recycler_view))
                .check(matches(atPosition(0, hasDescendant(withText("Test Text")))));

with method you may put into your Utils class.

public static Matcher<View> atPosition(final int position, @NonNull final Matcher<View> itemMatcher) {
    checkNotNull(itemMatcher);
    return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {
        @Override
        public void describeTo(Description description) {
            description.appendText("has item at position " + position + ": ");
            itemMatcher.describeTo(description);
        }

        @Override
        protected boolean matchesSafely(final RecyclerView view) {
            RecyclerView.ViewHolder viewHolder = view.findViewHolderForAdapterPosition(position);
            if (viewHolder == null) {
                // has no item on such position
                return false;
            }
            return itemMatcher.matches(viewHolder.itemView);
        }
    };
}

If your item may be not visible on the screen at first, then scroll to it before:

    onView(withId(R.id.recycler_view))
            .perform(scrollToPosition(87))
            .check(matches(atPosition(87, withText("Test Text"))));