43

I have a RecyclerView (R.id.recyclerView) where each row has an image (R.id.row_image) and a TextView. I want to click on the image in the first row.
I've tried to use onData(..) but it doesn't seem to work.

Mr-IDE
  • 7,051
  • 1
  • 53
  • 59
Jacki
  • 431
  • 1
  • 4
  • 3

10 Answers10

88

Use RecyclerViewActions

onView(withId(R.id.recyclerView))
    .perform(actionOnItemAtPosition(0, click()));

Include this in your gradle script:

dependencies {
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
    androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.0'
}
willcwf
  • 712
  • 7
  • 10
Gabor
  • 7,352
  • 4
  • 35
  • 56
21

Just to add to Gabor's answer (which is the correct and complete answer since Espresso 2.0).

You may encounter problems at the moment when using espresso-contrib and RecyclerViews (see android-test-kit ticket).

A workaround is to add this exclusion in the espresso-contrib dependency Gabor mentioned above:

androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.0') {
    exclude group: 'com.android.support', module: 'appcompat'
    exclude group: 'com.android.support', module: 'support-v4'
    exclude module: 'recyclerview-v7'
}

(This is an answer instead of a comment to Gabor's answer because I don't have the right to post comments yet)

Sir4ur0n
  • 1,753
  • 1
  • 13
  • 24
16

Edit:

Espresso 2.0 has been released, the changelog includes the following:

New Features

  • espresso-contrib
    • RecyclerViewActions: handles interactions with RecyclerViews

Old Answer

I haven't yet tested this myself, but Thomas Keller posted this on G+ with a short explanation and a link to a Gist with the necessary view matchers.

Since the new RecyclerView API inherits from ViewGroup and not from AdapterView, you cannot use Espresso's onData() to test layouts using this component.

Link to Gist.

I'll attach the code, just for completeness sake (note: not mine! All credit goes to Thomas Keller)

ViewMatcher:

public class ViewMatchers {
    @SuppressWarnings("unchecked")
    public static Matcher<View> withRecyclerView(@IdRes int viewId) {
        return allOf(isAssignableFrom(RecyclerView.class), withId(viewId));
    }

    @SuppressWarnings("unchecked")
    public static ViewInteraction onRecyclerItemView(@IdRes int identifyingView, Matcher<View> identifyingMatcher, Matcher<View> childMatcher) {
        Matcher<View> itemView = allOf(withParent(withRecyclerView(R.id.start_grid)),
                withChild(allOf(withId(identifyingView), identifyingMatcher)));
        return Espresso.onView(allOf(isDescendantOfA(itemView), childMatcher));
    }
}

And sample usage:

onRecyclerItemView(R.id.item_title, withText("Test"),  withId(R.id.item_content))
    .matches(check(withText("Test Content")));
Benoit
  • 5,118
  • 2
  • 24
  • 43
aried3r
  • 1,118
  • 13
  • 17
6

You should use a Custom ViewAction:

public void clickOnImageViewAtRow(int position) {
    onView(withId(R.id.recycler_view)).perform(RecyclerViewActions.actionOnItemAtPosition(position, new ClickOnImageView()));
}

public class ClickOnImageView implements ViewAction{
    ViewAction click = click();

    @Override
    public Matcher<View> getConstraints() {
        return click.getConstraints();
    }

    @Override
    public String getDescription() {
        return " click on custom image view";
    }

    @Override
    public void perform(UiController uiController, View view) {
        click.perform(uiController, view.findViewById(R.id.imageView));
    }
}
fede1608
  • 2,808
  • 1
  • 16
  • 17
4

Same answer like above but when some small modification since you have to provide the Viewholder type

With using RecyclerViewActions

fun tapOnRecyclerView(@IdRes resId: Int , position: Int) = onView(withId(resId))
        .perform(actionOnItemAtPosition<RecyclerView.ViewHolder>(position, click()));

and to include this libraries in your project , use this

androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"

Prefer to use the latest espresso version from this link

Andrew Emad
  • 277
  • 3
  • 13
3

I have found two ways:

  1. Assuming you have a textview with id "R.id.description" for each item in the RecyclerView. You can do this to match a specific child:

onView(allOf(withId(R.id.place_description),withText("what"))).perform(click());

  1. A tutorial from Android Testing Codelab https://codelabs.developers.google.com/codelabs/android-testing/#0

`

public Matcher<View> withItemText(final String itemText) {
        checkArgument(!TextUtils.isEmpty(itemText),"cannot be null");
        return new TypeSafeMatcher<View>() {
            @Override
            protected boolean matchesSafely(View item) {
                return allOf(isDescendantOfA(isAssignableFrom(RecyclerView.class)),withText(itemText)).matches(item);
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("is descendant of a RecyclerView with text" + itemText);
            }
        };
    }

`

And then, do this:

onView(withItemText("what")).perform(click());
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Luo Lei
  • 141
  • 1
  • 3
2

You needn't to add "testing-support-lib", neither "espresso:espresso-core". They are transitive added when "espresso:espresso-contrib" is added.

build.grade

dependencies {
    androidTestCompile 'com.android.support.test:runner:0.3'
    androidTestCompile 'com.android.support.test:rules:0.3'
    androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2'
}

Usage:

onView(withId(R.id.recyclerView)).perform(
            RecyclerViewActions.actionOnItemAtPosition(0, click()));
Víctor Albertos
  • 8,093
  • 5
  • 43
  • 71
1

I followed @Gabor's answer but when I included the libraries, I hit the dex limit!

So, I removed the libraries, added this getInstrumentation().waitForIdleSync(); and then just called onView(withId...))...

Works perfectly.

In your case you will have multiple image views with the same ID so you will have to figure out something on how you can select the particular list item.

Vedant Agarwala
  • 18,146
  • 4
  • 66
  • 89
1

As I posted here you can implement your custom RecyclerView matcher. Let's assume you have RecyclerView where each element has subject you wonna match:

public static Matcher<RecyclerView.ViewHolder> withItemSubject(final String subject) {
    Checks.checkNotNull(subject);
    return new BoundedMatcher<RecyclerView.ViewHolder, MyCustomViewHolder>(
            MyCustomViewHolder.class) {

        @Override
        protected boolean matchesSafely(MyCustomViewHolder viewHolder) {
            TextView subjectTextView = (TextView)viewHolder.itemView.findViewById(R.id.subject_text_view_id);

            return ((subject.equals(subjectTextView.getText().toString())
                    && (subjectTextView.getVisibility() == View.VISIBLE)));
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("item with subject: " + subject);
        }
    };
}

And usage:

onView(withId(R.id.my_recycler_view_id)
    .perform(RecyclerViewActions.actionOnHolderItem(withItemSubject("My subject"), click()));

Basically you can match anything you want. In this example we used subject TextView but it can be any element inside the RecyclerView item.

One more thing to clarify is check for visibility (subjectTextView.getVisibility() == View.VISIBLE). We need to have it because sometimes other views inside RecyclerView can have the same subject but it would be with View.GONE. This way we avoid multiple matches of our custom matcher and target only item that actually displays our subject.

Community
  • 1
  • 1
denys
  • 6,834
  • 3
  • 37
  • 36
1

With this code, you can scroll the recycler view to find your item withText and perform click or other action on it.

dependencies

androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.3.0'

Code

@Test
public void scrollRecyclerViewAndClick() {
    onView(withId(R.id.recycler_view)).
            perform(RecyclerViewActions.
            actionOnItem(withText("specific string"), click()));
}
Antoine Delia
  • 1,728
  • 5
  • 26
  • 42