Liste di caffè espresso

Espresso offre meccanismi per scorrere o agire su un particolare oggetto per due tipi di elenchi: viste adattatori e viste riciclatore.

Quando si gestiscono le liste, in particolare quelle create con un RecyclerView o un AdapterView oggetto, la visualizzazione che ti interessa potrebbe non essere nemmeno della schermata perché è visualizzato solo un numero limitato di bambini riciclato durante lo scorrimento. Il metodo scrollTo() non può essere utilizzato in questo caso perché richiede una vista esistente.

Interagire con le voci dell'elenco della visualizzazione adattatori

Anziché utilizzare il metodo onView(), inizia la tua ricerca con onData() e fornire un matcher per i dati su cui si basa la vista per la quale vuoi creare una corrispondenza. Espresso si occuperà di trovare la riga nell'oggetto Adapter e rendendo visibile l'elemento nell'area visibile.

Abbina i dati utilizzando un matcher vista personalizzato

L'attività seguente contiene un ListView, che è supportato da un SimpleAdapter che contiene i dati per ogni riga in un oggetto Map<String, Object>.

L&#39;attività dell&#39;elenco attualmente mostrata sullo schermo contiene un elenco di
          23 elementi. Ogni elemento ha un numero, memorizzato come stringa, mappato a una
          un numero diverso, archiviato invece come oggetto.

Ogni mappa ha due voci: una chiave "STR" contenente una stringa, come "item: x" e una chiave "LEN" che contiene Integer, che rappresenta la lunghezza dei contenuti. Ad esempio:

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

Il codice per un clic sulla riga con "item: 50" ha il seguente aspetto:

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

Tieni presente che Espresso scorre automaticamente l'elenco, se necessario.

Smontiamo Matcher<Object> all'interno di onData(). La is(instanceOf(Map.class)) restringe la ricerca a qualsiasi elemento di AdapterView, supportato da un oggetto Map.

Nel nostro caso, questo aspetto della query corrisponde a ogni riga della visualizzazione elenco, ma quando vuoi fare clic su un elemento specifico, restringiamo ulteriormente la ricerca con:

Kotlin

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

Java

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

Questo Matcher<String, Object> corrisponderà a qualsiasi mappa contenente una voce con la chiave "STR" e il valore "item: 50". Poiché il codice per cercare è e vogliamo riutilizzarlo in altre posizioni, creiamo un'immagine withItemContent corrispondente:

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

Utilizzi un BoundedMatcher come base perché per trovare corrispondenze solo per oggetti di tipo Map. Sostituisci il metodo matchesSafely() inserendo il matcher trovato e confrontarlo con un Matcher<String> che puoi passare come . Questo ti consente di chiamare withItemContent(equalTo("foo")). Per il codice brevità, puoi creare un altro matcher che chiama già equalTo() e accetta un oggetto String:

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

Ora il codice per fare clic sull'elemento è semplice:

Kotlin

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

Java

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

Per il codice completo di questo test, dai un'occhiata al metodo testClickOnItem50() all'interno AdapterViewTest classe e questa LongListMatchers personalizzata su GitHub.

Associare una vista secondaria specifica

L'esempio precedente genera un clic al centro dell'intera riga di un ListView. E se volessimo operare su un elemento figlio specifico della riga? Ad esempio, fare clic sulla seconda colonna della riga LongListActivity, che visualizza il valore String.length dei contenuti nella prima colonna:

In questo esempio, sarebbe utile estrarre solo la lunghezza
          un particolare contenuto. Questo processo comporta la determinazione
          della seconda colonna in una riga.

È sufficiente aggiungere una specifica onChildView() all'implementazione 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());

Interagire con gli elementi dell'elenco della visualizzazione del riciclatore

RecyclerView oggetti funzionano in modo diverso rispetto a AdapterView oggetti, quindi Impossibile utilizzare onData() per interagire.

Per interagire con RecyclerView utilizzando Espresso, puoi utilizzare lo espresso-contrib, che contiene una raccolta di RecyclerViewActions che può essere utilizzato per scorrere le posizioni o eseguire azioni sugli elementi:

  • scrollTo(): scorre fino alla vista corrispondente, se esistente.
  • scrollToHolder(): scorre fino al contenitore della vista corrispondente, se esistente.
  • scrollToPosition(): consente di scorrere fino a una posizione specifica.
  • actionOnHolderItem(): esegue un'azione di visualizzazione su un titolare della vista corrispondente.
  • actionOnItem(): esegue un'azione di visualizzazione su una vista con corrispondenza.
  • actionOnItemAtPosition(): esegue un'azione ViewAction su una vista in una posizione specifica.

I seguenti snippet presentano alcuni esempi Esempio di visualizzazione del riciclo esempio:

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

Risorse aggiuntive

Per ulteriori informazioni sull'utilizzo degli elenchi Espresso nei test Android, consulta il le seguenti risorse.

Campioni

  • DataAdapterSample: Mette in evidenza il punto di accesso onData() per Espresso, per liste e AdapterView di oggetti strutturati.