Article summary
My team recently added a RecyclerView to a screen in an Android app we’re working on. It’s a horizontal view that allows a user to scroll left and right to see content that’s offscreen. One of the challenges we’ve faced while working on this view has been testing it in our Espresso tests—specifically, testing the contents of items at certain positions. In this post, I’ll show you an Espresso matcher that can be used to aid in testing RecyclerViews.
RecyclerViewActions
The espresso-contrib
library provides a RecyclerViewActions class that offers a way to click on a specific position in a RecyclerView
(see instructions on configuring espress-contrib).
// Click item at position 3
onView(withId(R.id.scroll_view))
.perform(RecyclerViewActions.actionOnItemAtPosition(3, click()));
This is great for simulating a user who is tapping on an item in the RecyclerView
. But most of the tests I wanted to write had to do with verifying the contents of an item at a specific position. Just seeing that some content was on the screen wasn’t good enough. I needed to verify that certain content was showing up in position 0 or position 1, etc.
RecyclerViewMatcher
Using the RecyclerViewMatcher, you can not only perform actions on an item at a specific position in a RecyclerView
, but also check that some content is contained within a descendant of a specific item.
// Convenience helper
public static RecyclerViewMatcher withRecyclerView(final int recyclerViewId) {
return new RecyclerViewMatcher(recyclerViewId);
}
// Check item at position 3 has "Some content"
onView(withRecyclerView(R.id.scroll_view).atPosition(3))
.check(matches(hasDescendant(withText("Some content"))));
// Click item at position 3
onView(withRecyclerView(R.id.scroll_view).atPosition(3)).perform(click());
Testing Off-Screen Items
The RecyclerViewMatcher
matcher worked great until we tried testing items that were somewhere off-screen.
Our first improvement was to force the RecyclerView
to scroll so that the desired position was on-screen. The RecyclerViewActions
mentioned above provide a scrollToPosition
action that can do just that. Here’s a helper for tapping on a specific position that first scrolls and then taps:
public static void tapRecyclerViewItem(int recyclerViewId, int position) {
onView(withId(recyclerViewId)).perform(scrollToPosition(position));
onView(withRecyclerView(recyclerViewId).atPosition(position)).perform(click());
}
Doing the same when verifying the content of a RecyclerView
item also helped. But Espresso still seemed to have trouble verifying the content of an item whose index was greater than the number of items that could fit on the screen. For example, if four items at a time are visible on-screen, making assertions about the content of the item at position 10 would always fail.
The problem has to do with how the RecyclerViewMatcher
retrieves the View
at a specific position. It uses the getChildAt(int index)
method on RecyclerView
class (which is actually inherited from ViewGroup
). However, this method only appears to know about the items that are visible in the view. Whatever view is at the top is index 0, the second is index 1, etc.
An alternative is to use the findViewHolderForAdapterPosition(int position)
method to locate the view (by way of its ViewHolder
) for a specific position. This technique allows you to specify index 32 and have Espresso know which view you’re talking about.
Here’s an updated matchesSafely
method for the RecyclerViewMatcher
that uses this:
public boolean matchesSafely(View view) {
this.resources = view.getResources();
if (childView == null) {
RecyclerView recyclerView =
(RecyclerView) view.getRootView().findViewById(recyclerViewId);
if (recyclerView != null && recyclerView.getId() == recyclerViewId) {
RecyclerView.ViewHolder viewHolder =
recyclerView.findViewHolderForAdapterPosition(position);
if (viewHolder != null) {
childView = viewHolder.itemView;
}
}
else {
return false;
}
}
if (targetViewId == -1) {
return view == childView;
} else {
View targetView = childView.findViewById(targetViewId);
return view == targetView;
}
}
And here’s a gist for the entire updated RecycleViewMatcher helper.
Summary
By combining the RecyclerViewActions
provided by espresso-contrib
with Danny Roa’s RecyclerViewMatcher
(slightly modified), it’s possible to write comprehensive Espresso tests for an interface that uses a RecyclerView
, including verifying content on items at specific positions.
Note: After writing this post, I noticed that someone had already submitted a pull request to the RecyclerViewMatcher helper with pretty much this same change. It has not been accepted as of yet.
what if I don’t remember my item position in my recyclerview if I had more than 10 items in it.
Aravind – I’m not sure I understand what you’re asking. Can you clarify?
Thank you PATRICK
It is really useful
But How can we check for an item containing “some text” without specifying the position.
that is how to check if this String exist within any item.
Thanks again.
No, this doesnt work for me, am I doing something wrong?
onView(withRecyclerView(R.id.synology_apps_rv)
.atPositionOnView(7, R.id.not_installed_tv))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)));
android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: with id: be.vanlooverenkoen.automatedsyno.dev:id/synology_apps_rv
View Hierarchy:
+>DecorView{id=-1, visibility=VISIBLE, width=1080, height=1920, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=3}
|
+->LinearLayout{id=-1, visibility=VISIBLE, width=1080, height=1794, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=2}
|
+–>ViewStub{id=16909288, res-name=action_mode_bar_stub, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0}
|
+–>FrameLayout{id=-1, visibility=VISIBLE, width=1080, height=1731, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=63.0, child-count=1}
|
+—>ActionBarOverlayLayout{id=2131427396, res-name=decor_content_parent, visibility=VISIBLE, width=1080, height=1731, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=2}
|
+—->ContentFrameLayout{id=16908290, res-name=content, visibility=VISIBLE, width=1080, height=1584, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=147.0, child-count=1}
|
+—–>LinearLayout{id=-1, visibility=VISIBLE, width=1080, height=1584, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}
|
+——>RecyclerView{id=2131427413, res-name=synology_apps_rv, visibility=VISIBLE, width=1080, height=1584, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=true, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=4}
|
+——->LinearLayout{id=-1, visibility=VISIBLE, width=1028, height=438, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=26.0, y=13.0, child-count=3}
|
+——–>AppCompatTextView{id=2131427438, res-name=installed_tv, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Installed, input-type=0, ime-target=false, has-links=false}
|
+——–>AppCompatTextView{id=2131427439, res-name=not_installed_tv, visibility=VISIBLE, width=387, height=71, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Not Installed, input-type=0, ime-target=false, has-links=false}
|
+——–>CardView{id=2131427440, res-name=cardview, visibility=VISIBLE, width=1028, height=367, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=71.0, child-count=1}
|
+———>RelativeLayout{id=-1, visibility=VISIBLE, width=1014, height=347, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=7.0, y=10.0, child-count=2}
|
+———->AppCompatImageView{id=2131427441, res-name=synology_app_icon, desc=Synolgy App Icon, visibility=VISIBLE, width=263, height=263, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=42.0, y=42.0}
|
+———->AppCompatTextView{id=2131427442, res-name=name_tv, visibility=VISIBLE, width=218, height=71, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=331.0, y=138.0, text=DS Video, input-type=0, ime-target=false, has-links=false}
|
+——->LinearLayout{id=-1, visibility=VISIBLE, width=1028, height=367, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=26.0, y=477.0, child-count=3}
|
+——–>AppCompatTextView{id=2131427438, res-name=installed_tv, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Installed, input-type=0, ime-target=false, has-links=false}
|
+——–>AppCompatTextView{id=2131427439, res-name=not_installed_tv, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Not Installed, input-type=0, ime-target=false, has-links=false}
|
+——–>CardView{id=2131427440, res-name=cardview, visibility=VISIBLE, width=1028, height=367, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}
|
+———>RelativeLayout{id=-1, visibility=VISIBLE, width=1014, height=347, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=7.0, y=10.0, child-count=2}
|
+———->AppCompatImageView{id=2131427441, res-name=synology_app_icon, desc=Synolgy App Icon, visibility=VISIBLE, width=263, height=263, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=42.0, y=42.0}
|
+———->AppCompatTextView{id=2131427442, res-name=name_tv, visibility=VISIBLE, width=220, height=71, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=331.0, y=138.0, text=DS Audio, input-type=0, ime-target=false, has-links=false}
|
+——->LinearLayout{id=-1, visibility=VISIBLE, width=1028, height=367, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=26.0, y=870.0, child-count=3}
|
+——–>AppCompatTextView{id=2131427438, res-name=installed_tv, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Installed, input-type=0, ime-target=false, has-links=false}
|
+——–>AppCompatTextView{id=2131427439, res-name=not_installed_tv, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Not Installed, input-type=0, ime-target=false, has-links=false}
|
+——–>CardView{id=2131427440, res-name=cardview, visibility=VISIBLE, width=1028, height=367, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}
|
+———>RelativeLayout{id=-1, visibility=VISIBLE, width=1014, height=347, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=7.0, y=10.0, child-count=2}
|
+———->AppCompatImageView{id=2131427441, res-name=synology_app_icon, desc=Synolgy App Icon, visibility=VISIBLE, width=263, height=263, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=42.0, y=42.0}
|
+———->AppCompatTextView{id=2131427442, res-name=name_tv, visibility=VISIBLE, width=111, height=71, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=331.0, y=138.0, text=Chat, input-type=0, ime-target=false, has-links=false}
|
+——->LinearLayout{id=-1, visibility=VISIBLE, width=1028, height=367, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=26.0, y=1263.0, child-count=3}
|
+——–>AppCompatTextView{id=2131427438, res-name=installed_tv, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Installed, input-type=0, ime-target=false, has-links=false}
|
+——–>AppCompatTextView{id=2131427439, res-name=not_installed_tv, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Not Installed, input-type=0, ime-target=false, has-links=false}
|
+——–>CardView{id=2131427440, res-name=cardview, visibility=VISIBLE, width=1028, height=367, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}
|
+———>RelativeLayout{id=-1, visibility=VISIBLE, width=1014, height=347, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=7.0, y=10.0, child-count=2}
|
+———->AppCompatImageView{id=2131427441, res-name=synology_app_icon, desc=Synolgy App Icon, visibility=VISIBLE, width=263, height=263, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=42.0, y=42.0}
|
+———->AppCompatTextView{id=2131427442, res-name=name_tv, visibility=VISIBLE, width=163, height=71, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=331.0, y=138.0, text=DS Get, input-type=0, ime-target=false, has-links=false}
|
+—->ActionBarContainer{id=2131427397, res-name=action_bar_container, visibility=VISIBLE, width=1080, height=147, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=2}
|
+—–>Toolbar{id=2131427398, res-name=action_bar, visibility=VISIBLE, width=1080, height=147, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=3}
|
+——>AppCompatTextView{id=-1, visibility=VISIBLE, width=351, height=71, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=189.0, y=38.0, text=Synology Apps, input-type=0, ime-target=false, has-links=false}
|
+——>AppCompatImageButton{id=-1, desc=Navigate up, visibility=VISIBLE, width=147, height=147, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0}
|
+——>ActionMenuView{id=-1, visibility=VISIBLE, width=0, height=147, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=1080.0, y=0.0, child-count=0}
|
+—–>ActionBarContextView{id=2131427399, res-name=action_context_bar, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=0}
|
+->View{id=16908336, res-name=navigationBarBackground, visibility=VISIBLE, width=1080, height=126, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=1794.0}
|
+->View{id=16908335, res-name=statusBarBackground, visibility=VISIBLE, width=1080, height=63, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0}
|
at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:1566)
at android.support.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:92)
at android.support.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:56)
at android.support.test.espresso.ViewInteraction.runSynchronouslyOnUiThread(ViewInteraction.java:184)
at android.support.test.espresso.ViewInteraction.check(ViewInteraction.java:158)
at be.vanlooverenkoen.automatedsyno.settings.ui.TestSynologyAppActivity.testSynologyAppList(TestSynologyAppActivity.java:30)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at android.support.test.internal.statement.UiThreadStatement.evaluate(UiThreadStatement.java:55)
at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:270)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:59)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:262)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1932)
Koen,
Unfortunately nothing obvious jumps out as wrong in your usage. I will say that I’ve never used the
atPositionOnView
version, onlyatPosition
and then usedhasDescendant
in the matcher. I don’t know if it will make any difference, but I might try something like this just to see:Oh, and if item 7 is off screen don’t forget to scroll before you do the check:
Hi Mr.Patrick,
Happy new year, Let me share my query, In my present project, i have implemented view-pager with 3 tabs, On my first tab required design to show first position i always show cover image of user, i tried to implement recycler view but not working perfectly, so place nested scrollview as parent, and Listview as child, on top of listview i have image view to show cover image, can u please gimme suggestion about the design using Recyclerview. I know this is not related the your blog, but i’m help less. if i post my query in stack overflow, they down voted and closed my question. Thankyou. Sorry if anything i asked anonymously.
Have you tried the latest contrib repo – https://developer.android.com/reference/android/support/test/espresso/contrib/RecyclerViewActions.html and does it still repro your problem?
As of 2016-07-12, that PR (https://github.com/dannyroa/espresso-samples/pull/1) has been merged.
This has no sense when you have more than one recycler view with the same id.
Hi is there any possible to test the recyclerview that create dynamically like
Recyclerview rv = new RecyclerView();