Espresso - how to find a specific item in a recycler view (order is random)
Solution 1
I was able to get this to work doing the following:
Matcher<RecyclerView.ViewHolder> matcher = CustomMatcher.withTitle("A");
onView((withId(R.id.recycler_view))).perform(scrollToHolder(matcher), actionOnHolderItem(matcher, click()));
Where CustomMatcher.withTitle
is:
public static Matcher<RecyclerView.ViewHolder> withTitle(final String title)
{
return new BoundedMatcher<RecyclerView.ViewHolder, CustomListAdapter.ItemViewHolder>(CustomListAdapter.ItemViewHolder.class)
{
@Override
protected boolean matchesSafely(CustomListAdapter.ItemViewHolder item)
{
return item.mTitleView.getText().toString().equalsIgnoreCase(title);
}
@Override
public void describeTo(Description description)
{
description.appendText("view holder with title: " + title);
}
};
}
Solution 2
You don't need a custom matcher for this, just use this
onView(withId(R.id.recycler_id)).perform(RecyclerViewActions.actionOnItem(hasDescendant(withText("A")), click()));
and add espresso-contrib dependency
androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:<espressso-version>'
Or if you're using AndroidX
androidTestImplementation 'androidx.test.espresso:espresso-contrib:<espressso-version>'
Solution 3
I know it's an old question, while @Zach's answer is a good start, and help me in my answer, we have a recycler view with loads of different types of ViewHolders with different Ids for the texts the answer did not fully suit our use case.
What I ended up doing was using a slightly different matcher, which does not reference any specific Ids or variables:
fun withText(title: String) = object : BoundedMatcher<View, View>(View::class.java) {
override fun describeTo(description: Description?) {
description?.appendText("Searching for title with: $title")
}
override fun matchesSafely(item: View?): Boolean {
val views = ArrayList<View>()
item?.findViewsWithText(views, title, View.FIND_VIEWS_WITH_TEXT)
return when {
views.size == 1 -> true
else -> false
}
}
}
And usage:
RecyclerViewActions.scrollTo<ViewHolder>(withText("Some unique text I'm looking for")
Comments
-
Zach almost 2 years
I'm wondering how I would be able to find a specific item in a recycler view where the order of items is randomized each run.
Let's assume I have 4 items in the recycler view, each represented by the same type of view holder with a text view in it. A unique title is applied to each view holder/item. For this example let's say the titles are, for simplicity's sake, "A", "B", "C", and "D".
How would I find the position (and then click) item "A" if the order is randomized? I know if the order does not change I could the scrollToPosition RecyclerViewInteraction action, but in this case the order can and will change.
Any thoughts?
-
Zach almost 8 yearsSounds like a good approach, but I'm getting an error trying to use this:
android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: is assignable from class: class android.widget.AdapterView
-
Zach almost 8 yearsAh, I don't think onData can be used for recycler views, is that correct?
-
njzk2 almost 8 yearsright. I would have thought it could have worked. there this, though: stackoverflow.com/questions/27396583/…
-
Zach almost 8 yearsThanks, this led me to figuring out a way to handle it... will post in an answer now!
-
tir38 over 5 yearsThis only works because
CustomListAdapter.ItemViewHolder
exposesmTitleView
as a public field. If you only do this for testing then you are leaking test code into the app. -
Conner almost 4 yearsVery helpful. I couldn't figure out how to find an item by text.
-
Abhinav Tyagi about 3 yearsthis will not work when the item is not in view
-
Anass NAZIH about 3 yearsIt will scroll to find the item first then click it
-
Slartibartfast over 2 yearsDoes not scroll for me.
-
Carson Holzheimer over 2 yearsI honestly can't believe this was the only place I found this information. Why isn't this the default way to test lists :(