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(Rec
Just to enhance riwnodennyk's answer, if your ViewHolder
is a ViewGroup
instead of a direct View
object like TextView
, then you can add hasDescendant
matcher to match the TextView
object in the ViewGroup
. Example:
onView(withId(R.id.recycler_view))
.check(matches(atPosition(0, hasDescendant(withText("First Element")))));
I combined a few answers above into a reusable method for testing other recycler views as the project grows. Leaving the code here in the event it helps anyone else out...
Declare a function that may be called on a RecyclerView Resource ID:
fun Int.shouldHaveTextAtPosition(text:String, position: Int) {
onView(withId(this))
.perform(scrollToPosition<RecyclerView.ViewHolder>(position))
.check(matches(atPosition(position, hasDescendant(withText(text)))))
}
Then call it on your recycler view to check the text displayed of multiple adapter items in the list at a given position by doing:
with(R.id.recycler_view_id) {
shouldHaveTextAtPosition("Some Text at position 0", 0)
shouldHaveTextAtPosition("Some Text at position 1", 1)
shouldHaveTextAtPosition("Some Text at position 2", 2)
shouldHaveTextAtPosition("Some Text at position 3", 3)
}
This thread is pretty old but I recently extended the RecyclerViewAction support to be usable for assertions as well. This allows you to test off-screen elements without specifying a position while still using view matchers.
Check out this article > Better Android RecyclerView Testing!
Danny Roa's solution is awesome, but it didn't work for the off-screen items I had just scrolled to. The fix is replacing this:
View targetView = recyclerView.getChildAt(this.position)
.findViewById(this.viewId);
with this:
View targetView = recyclerView.findViewHolderForAdapterPosition(this.position)
.itemView.findViewById(this.viewId);
... in the TestUtils class.
I have been struggling on the same issue where I have a recycler view with 6 items and then I have to choose the one at position 5 and the 5th one is not visible on the view.
The above solution also works. Also, I know its a bit late but thought its worth sharing.
I did this with a simple solution as below:
onView(withId(R.id.recylcer_view))
.perform(RecyclerViewActions.actionOnItem(
hasDescendant(withText("Text of item you want to scroll to")),
click()));
RecyclerViewActions.actionOnItem
-
Performs a ViewAction
on a view matched by viewHolderMatcher
.
RecyclerView
to the view matched by itemViewMatcher
More details can be found at : RecyclerViewActions.actionOnItem
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"))));