wake-up-neo.com

So klicken Sie in Espresso auf ein Element in einer RecyclerView

Ich habe eine RecyclerView (R.id.recyclerView), in der jede Zeile ein Bild (R.id.row_image) und eine TextView hat. Ich möchte auf das Bild in der ersten Reihe klicken.
Ich habe versucht, onData (..) zu verwenden, aber es scheint nicht zu funktionieren.

33
Jacki

Verwenden Sie RecyclerViewActions

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

Fügen Sie dies in Ihr Gradle-Skript ein:

dependencies {
    androidTestCompile 'com.Android.support.test:testing-support-lib:0.1'
    androidTestCompile 'com.Android.support.test.espresso:espresso-core:2.0'
    androidTestCompile 'com.Android.support.test.espresso:espresso-contrib:2.0'
}
69
Gabor

Nur um Gabors Antwort hinzuzufügen (was seit Espresso 2.0 die richtige und vollständige Antwort ist).

Bei der Verwendung von espresso-contrib und RecyclerViews können derzeit Probleme auftreten (siehe Android-Test-Kit-Ticket ). 

Eine Problemumgehung besteht darin, diesen Ausschluss in der oben genannten espresso-contrib-Abhängigkeit von Gabor hinzuzufügen:

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'
}

(Dies ist eine Antwort statt eines Kommentars zu Gabors Antwort, da ich noch nicht das Recht habe, Kommentare zu schreiben.)

21
Sir4ur0n

Bearbeiten:

Espresso 2.0 wurde veröffentlicht, das Änderungsprotokoll enthält Folgendes:

Neue Eigenschaften

  • espresso-Beitrag
    • RecyclerViewActions: Behandelt Interaktionen mit RecyclerViews

Alte Antwort

Ich habe das noch nicht selbst getestet, aber Thomas Keller postete dies auf G + mit einer kurzen Erklärung und einem Link zu einem Gist mit den erforderlichen View-Matchern.

Da die neue RecyclerView-API von ViewGroup und nicht von AdapterView erbt, können Sie Espressos onData() nicht zum Testen von Layouts mit dieser Komponente verwenden.

Link zu Gist .

Ich werde den Code nur der Vollständigkeit halber beifügen (Anmerkung: nicht meins! Alle Gutschriften gehen an 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));
    }
}

Und beispielverwendung:

onRecyclerItemView(R.id.item_title, withText("Test"),  withId(R.id.item_content))
    .matches(check(withText("Test Content")));
15
aried3r

Sie sollten eine benutzerdefinierte ViewAction verwenden:

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));
    }
}
5
fede1608

Ich habe zwei Möglichkeiten gefunden:

  1. Angenommen, Sie haben eine Textansicht mit der ID "R.id.description" für jedes Element in der RecyclerView. Sie können dies tun, um ein bestimmtes Kind zu finden:

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

  1. Ein Tutorial von Android Testing Codelab https://codelabs.developers.google.com/codelabs/Android-testing/#

`

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);
            }
        };
    }

`

Und dann mach das:

onView(withItemText("what")).perform(click());
2
Luo Lei

Ich habe die Antwort von @ Gabor befolgt, aber als ich die Bibliotheken aufgenommen habe, habe ich das Dex-Limit erreicht!

Also entfernte ich die Bibliotheken, fügte diese getInstrumentation().waitForIdleSync(); hinzu und rief dann einfach onView(withId...))... an.

Funktioniert perfekt.

In Ihrem Fall haben Sie mehrere Bildansichten mit derselben ID, so dass Sie etwas darüber herausfinden müssen, wie Sie den jeweiligen Listeneintrag auswählen können.

1
vedant

Sie müssen weder "testing-support-lib" noch "espresso: espresso-core" hinzufügen. Sie werden transitiv hinzugefügt, wenn "Espresso: Espresso-Contrib" hinzugefügt wird.

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'
}

Verwendungszweck :

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

Mit here können Sie Ihren benutzerdefinierten RecyclerView-Matcher implementieren. Nehmen wir an, Sie haben RecyclerView, wo jedes Element ein Thema hat, für das Sie keinen Treffer haben:

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);
        }
    };
}

Und Verwendung:

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

Grundsätzlich können Sie alles anpassen, was Sie möchten. In diesem Beispiel haben wir den Betreff TextView verwendet, es kann sich jedoch um ein beliebiges Element im Element RecyclerView handeln.

Eine weitere Sache zu klären ist die Überprüfung der Sichtbarkeit (subjectTextView.getVisibility() == View.VISIBLE). Wir müssen es haben, weil manchmal andere Ansichten in RecyclerView dasselbe Thema haben können, aber es wäre mit View.GONE. Auf diese Weise vermeiden wir mehrere Übereinstimmungen mit unserem benutzerdefinierten Matcher und einem Zielobjekt, das tatsächlich unser Thema anzeigt.

1
denys