Espressolisten

Espresso bietet zwei Möglichkeiten, zu einem bestimmten Element zu scrollen oder eine Aktion auszuführen. Listentypen: Adapteransichten und Recycler-Ansichten.

Beim Umgang mit Listen, insbesondere solchen, die mit einem RecyclerView oder einem AdapterView-Objekt enthält, befindet sich die Ansicht, an der Sie interessiert sind, möglicherweise da nur eine kleine Anzahl untergeordneter Elemente wenn Sie scrollen. Die Methode scrollTo() kann in diesem Fall nicht verwendet werden da hierfür eine vorhandene Ansicht erforderlich ist.

Mit Listenelementen der Adapteransicht interagieren

Anstatt die Methode onView() zu verwenden, beginnen Sie Ihre Suche mit onData() und einen Abgleich mit den Daten bereitstellen, die die zu abgleichende Ansicht unterstützen. Espresso sucht dann die Zeile im Adapter-Objekt und sodass das Element im Darstellungsbereich sichtbar wird.

Daten mithilfe eines benutzerdefinierten Abgleichs-Matchers abgleichen

Die Aktivität unten enthält ein ListView-Element mit einem SimpleAdapter-Element enthält Daten für jede Zeile in einem Map<String, Object>-Objekt.

Die aktuell auf dem Bildschirm angezeigte Listenaktivität enthält eine Liste mit
          23 Elemente. Jedes Element hat eine Zahl, die als String gespeichert und einem
          eine andere Nummer hat, die stattdessen als Objekt gespeichert wird.

Jede Zuordnung hat zwei Einträge: einen Schlüssel-"STR", der einen String enthält, z. B. "item: x" und einem Schlüssel "LEN", der eine Integer enthält, die den Länge des Inhalts. Beispiel:

{"STR" : "item: 0", "LEN": 7}

Der Code für einen Klick auf die Zeile mit „item: 50“ sieht so aus:

Kotlin

onData(allOf(`is`(instanceOf(Map::class.java)), hasEntry(equalTo("STR"),
        `is`("item: 50")))).perform(click())

Java

onData(allOf(is(instanceOf(Map.class)), hasEntry(equalTo("STR"), is("item: 50"))))
    .perform(click());

Beachten Sie, dass Espresso bei Bedarf automatisch durch die Liste scrollt.

Nehmen wir das Matcher<Object> in onData() auseinander. Die is(instanceOf(Map.class)) beschränkt die Suche auf ein beliebiges Element der AdapterView, die von einem Map-Objekt gestützt wird.

In unserem Fall stimmt dieser Aspekt der Abfrage mit jeder Zeile der Listenansicht überein, auf ein Element klicken möchten, grenzen wir die Suche daher weiter ein:

Kotlin

hasEntry(equalTo("STR"), `is`("item: 50"))

Java

hasEntry(equalTo("STR"), is("item: 50"))

Dieses Matcher<String, Object> stimmt mit jeder Karte überein, die einen Eintrag mit den Schlüssel "STR" und den Wert "item: 50". Da der Code zum Suchen und sie auch an anderen Stellen wiederverwenden möchten. Schreiben wir nun eine benutzerdefinierte withItemContent-Matcher dafür:

Kotlin

return object : BoundedMatcher<Object, Map>(Map::class.java) {
    override fun matchesSafely(map: Map): Boolean {
        return hasEntry(equalTo("STR"), itemTextMatcher).matches(map)
    }

    override fun describeTo(description: Description) {
        description.appendText("with item content: ")
        itemTextMatcher.describeTo(description)
    }
}

Java

return new BoundedMatcher<Object, Map>(Map.class) {
    @Override
    public boolean matchesSafely(Map map) {
        return hasEntry(equalTo("STR"), itemTextMatcher).matches(map);
    }

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

Sie verwenden eine BoundedMatcher als Basis, da nur Objekte des Typs abgeglichen werden Map. matchesSafely()-Methode überschreiben und den gefundenen Matcher einfügen und sie mit einer Matcher<String> abgleichen, die Sie als . So können Sie withItemContent(equalTo("foo")) aufrufen. Für Code können Sie einen weiteren Matcher erstellen, der bereits die equalTo() und akzeptiert ein String-Objekt:

Kotlin

fun withItemContent(expectedText: String): Matcher<Object> {
    checkNotNull(expectedText)
    return withItemContent(equalTo(expectedText))
}

Java

public static Matcher<Object> withItemContent(String expectedText) {
    checkNotNull(expectedText);
    return withItemContent(equalTo(expectedText));
}

Jetzt ist der Code zum Klicken auf das Element einfach:

Kotlin

onData(withItemContent("item: 50")).perform(click())

Java

onData(withItemContent("item: 50")).perform(click());

Den vollständigen Code für diesen Test finden Sie in der testClickOnItem50()-Methode im AdapterViewTest Klasse und dieser benutzerdefinierten LongListMatchers Matcher auf GitHub.

Mit einer bestimmten untergeordneten Ansicht abgleichen

Das Beispiel oben gibt einen Klick in der Mitte der gesamten Zeile eines ListView aus. Aber was ist, wenn wir für ein bestimmtes untergeordnetes Element der Zeile arbeiten möchten? Zum Beispiel auf die zweite Spalte in der Zeile von LongListActivity klicken möchte, mit der String.length des Inhalts in der ersten Spalte angezeigt wird:

In diesem Beispiel wäre es sinnvoll, nur die Länge der
          bestimmten Inhalts. Bei diesem Prozess wird festgelegt,
          Wert der zweiten Spalte einer Zeile.

Fügen Sie einfach eine onChildView()-Spezifikation zu Ihrer Implementierung von DataInteraction:

Kotlin

onData(withItemContent("item: 60"))
    .onChildView(withId(R.id.item_size))
    .perform(click())

Java

onData(withItemContent("item: 60"))
    .onChildView(withId(R.id.item_size))
    .perform(click());

Mit Listenelementen der Recycler-Ansicht interagieren

RecyclerView-Objekte funktionieren anders als AdapterView-Objekte. onData() kann nicht für die Interaktion mit ihnen verwendet werden.

Um über Espresso mit RecyclerViews zu interagieren, können Sie die espresso-contrib-Paket mit einer Sammlung von RecyclerViewActions mit dem Sie zu bestimmten Positionen scrollen oder Aktionen für Elemente ausführen können:

  • scrollTo(): Hiermit wird zur übereinstimmenden Ansicht gescrollt, sofern vorhanden.
  • scrollToHolder(): Hiermit wird zum übereinstimmenden Ansichtsinhaber gescrollt, sofern vorhanden.
  • scrollToPosition(): Scrollt zu einer bestimmten Position.
  • actionOnHolderItem(): Führt eine Aufrufaktion für einen übereinstimmenden View-Holder aus.
  • actionOnItem(): Führt eine Aufrufaktion für einen übereinstimmenden Aufruf aus.
  • actionOnItemAtPosition(): Führt eine ViewAction für einen Aufruf an einer bestimmten Position aus.

Die folgenden Snippets enthalten einige Beispiele aus dem RecyclerViewSample Beispiel:

Kotlin

@Test(expected = PerformException::class)
fun itemWithText_doesNotExist() {
    // Attempt to scroll to an item that contains the special text.
    onView(ViewMatchers.withId(R.id.recyclerView))
        .perform(
            // scrollTo will fail the test if no item matches.
            RecyclerViewActions.scrollTo(
                hasDescendant(withText("not in the list"))
            )
        )
}

Java

@Test(expected = PerformException.class)
public void itemWithText_doesNotExist() {
    // Attempt to scroll to an item that contains the special text.
    onView(ViewMatchers.withId(R.id.recyclerView))
            // scrollTo will fail the test if no item matches.
            .perform(RecyclerViewActions.scrollTo(
                    hasDescendant(withText("not in the list"))
            ));
}

Kotlin

@Test fun scrollToItemBelowFold_checkItsText() {
    // First, scroll to the position that needs to be matched and click on it.
    onView(ViewMatchers.withId(R.id.recyclerView))
        .perform(
            RecyclerViewActions.actionOnItemAtPosition(
                ITEM_BELOW_THE_FOLD,
                click()
            )
        )

    // Match the text in an item below the fold and check that it's displayed.
    val itemElementText = "${activityRule.activity.resources
        .getString(R.string.item_element_text)} ${ITEM_BELOW_THE_FOLD.toString()}"
    onView(withText(itemElementText)).check(matches(isDisplayed()))
}

Java

@Test
public void scrollToItemBelowFold_checkItsText() {
    // First, scroll to the position that needs to be matched and click on it.
    onView(ViewMatchers.withId(R.id.recyclerView))
            .perform(RecyclerViewActions.actionOnItemAtPosition(ITEM_BELOW_THE_FOLD,
            click()));

    // Match the text in an item below the fold and check that it's displayed.
    String itemElementText = activityRule.getActivity().getResources()
            .getString(R.string.item_element_text)
            + String.valueOf(ITEM_BELOW_THE_FOLD);
    onView(withText(itemElementText)).check(matches(isDisplayed()));
}

Kotlin

@Test fun itemInMiddleOfList_hasSpecialText() {
    // First, scroll to the view holder using the isInTheMiddle() matcher.
    onView(ViewMatchers.withId(R.id.recyclerView))
        .perform(RecyclerViewActions.scrollToHolder(isInTheMiddle()))

    // Check that the item has the special text.
    val middleElementText = activityRule.activity.resources
            .getString(R.string.middle)
    onView(withText(middleElementText)).check(matches(isDisplayed()))
}

Java

@Test
public void itemInMiddleOfList_hasSpecialText() {
    // First, scroll to the view holder using the isInTheMiddle() matcher.
    onView(ViewMatchers.withId(R.id.recyclerView))
            .perform(RecyclerViewActions.scrollToHolder(isInTheMiddle()));

    // Check that the item has the special text.
    String middleElementText =
            activityRule.getActivity().getResources()
            .getString(R.string.middle);
    onView(withText(middleElementText)).check(matches(isDisplayed()));
}

Weitere Informationen

Weitere Informationen zur Verwendung von Espressolisten in Android-Tests finden Sie in der in den folgenden Ressourcen.

Produktproben

  • DataAdapterSample: Zeigt den onData()-Einstiegspunkt für Espresso, für Listen und AdapterView Objekte.