Reputation: 10364
I'm running an Espresso test to click on an element of the RecyclerView.
onView(withId(R.id.recyclerList)).perform(RecyclerViewActions.actionOnItemAtPosition(2, click()));
However, I'm getting the following error: java.lang.IllegalStateException: No view holder at position
I've checked the view id is pointing to my RecyclerView and my dependencies are as follows:
Upvotes: 9
Views: 2112
Reputation: 551
You can use the CountingIdlingResource
class that will make sure that your data is loaded in your recycler view before running the test by calling increment()
on your counting idle object before fetching the data, then calling decrement()
on your counting idle object after the data has been loaded.
The first thing to do is to add the dependency to use the CountingIdlingResource
implementation 'androidx.test.espresso:espresso-idling-resource:3.3.0'
In your Activity define a global public variable of type CountingIdlingResource
public CountingIdlingResource idlingResource = new CountingIdlingResource("Loader");
"Loader" is just a unique id for your Idling Resource (From docs: the resource name this resource should report to Espresso.)
Then before fetching your data from the data source just call idlingResource.increment()
and after the data is fetched just call idlingResource.decrement()
The final Step inside your RecyclerViewTest class is to add your activity rule
@Rule
public ActivityScenarioRule<YourActivity> rule = new ActivityScenarioRule<>(YourActivity.class);
then inside your test method
@Test
public void recyclerViewItemClick() {
rule.getScenario().onActivity(new ActivityScenario.ActivityAction<YourActivity>() {
@Override
public void perform(YourActivity activity) {
IdlingRegistry.getInstance().register(activity.idlingResource);
}
});
onView(withId(R.id.rv)).perform(actionOnItemAtPosition(2, click()));
}
Upvotes: 0
Reputation: 152
I guess, the possible issue is that your item actually not visible when you are trying to perform your action. If so, you can try onView(withId(R.id.recyclerList)).perform(RecyclerViewActions.scrollToPosition(2))
and then perform your action
Upvotes: 1
Reputation: 605
Ok, I think I had the same problem and I managed to solve it.
The problem was that unless you are using an AsyncTask to populate the RecyclerView, Espresso doesn't wait for the background thread to complete before performing the next operation.
So, it would search for the item at position 2, and since your background thread is still running and the recycler view hasn't loaded yet, it would return the IllegalStateException.
Even though I wouldn't suggest using this, you can check if the test works very quickly, by adding a Thread.sleep() (with a reasonable time), like this:
@Test
public void myTest() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
onView(withId(R.id.recyclerList))
.perform(RecyclerViewActions.actionOnItemAtPosition(2, click()));
//assert something
}
The better (and correct) solution to this problem, is using Idling Resources.
This short and concise video really helped me understand how to use IdlingResources (CountingIdlingResources in particular).
Your ui test should now look something like:
@Test
public void myTest() {
registerIdlingResources(/*put your IdlingResource here*/);
onView(withId(R.id.recyclerList))
.perform(RecyclerViewActions.actionOnItemAtPosition(2, click()));
//assert something
}
I hope it helped.
Upvotes: 6