Espresso offre meccanismi per scorrere o agire su un determinato elemento per due tipi di elenchi: visualizzazioni adattatore e visualizzazioni riciclo.
Quando hai a che fare con elenchi, in particolare quelli creati con un oggetto RecyclerView
o AdapterView
, la visualizzazione che ti interessa potrebbe non apparire nemmeno sullo schermo perché solo un numero limitato di bambini viene mostrato e viene riciclato mentre scorri. Il metodo scrollTo()
non può essere utilizzato in questo caso
perché richiede una vista esistente.
Interagire con gli elementi dell'elenco della visualizzazione adattatore
Anziché utilizzare il metodo onView()
, inizia la ricerca con onData()
e
fornisci un matcher con i dati a supporto della vista per cui vuoi trovare una corrispondenza.
Espresso si occuperà di trovare la riga nell'oggetto Adapter
e di rendere l'elemento visibile nell'area visibile.
Abbina i dati utilizzando un matcher visualizzazione personalizzata
L'attività seguente contiene un elemento ListView
, supportato da un SimpleAdapter
che contiene i dati di ogni riga in un oggetto Map<String, Object>
.
Ogni mappa ha due voci: una chiave "STR"
che contiene una stringa, ad esempio
"item: x"
, e una chiave "LEN"
che contiene un Integer
, che rappresenta la
lunghezza dei contenuti. Ecco alcuni esempi:
{"STR" : "item: 0", "LEN": 7}
Il codice per un clic sulla riga contenente "elemento: 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 in base alle esigenze.
Smontiamo Matcher<Object>
all'interno di onData()
. Il metodo is(instanceOf(Map.class))
restringe la ricerca a qualsiasi elemento di AdapterView
, che è supportato da un oggetto Map
.
Nel nostro caso, questo aspetto della query corrisponde a ogni riga della visualizzazione elenco, ma vogliamo fare clic specificamente su un elemento, quindi 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 da cercare è lungo e vogliamo utilizzarlo in altre località, scriviamo un matcher withItemContent
personalizzato:
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é devi abbinare solo gli oggetti di tipo Map
. Sostituisci il metodo matchesSafely()
, inserendo il matcher trovato
in precedenza e abbinalo a un Matcher<String>
che puoi passare come
argomento. In questo modo puoi chiamare withItemContent(equalTo("foo"))
. Per aumentare la visibilità del codice, puoi creare un altro matcher che chiami già equalTo()
e accetti 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()
nella classe
AdapterViewTest
e a
questo matcher LongListMatchers
personalizzato su GitHub.
Abbina una vista secondaria specifica
L'esempio riportato sopra genera un clic al centro dell'intera riga di un elemento ListView
.
E se volessimo operare su un elemento secondario specifico della riga? Ad esempio, fai clic sulla seconda colonna della riga LongListActivity
, che visualizza il valore String.length del contenuto nella prima colonna:
È sufficiente aggiungere una specifica onChildView()
all'implementazione di
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 dello strumento di riciclo
Gli oggetti RecyclerView
funzionano in modo diverso rispetto agli oggetti AdapterView
, pertanto
onData()
non può essere utilizzato per interagire con loro.
Per interagire con RecyclerViews utilizzando Espresso, puoi usare il pacchetto espresso-contrib
, che contiene una raccolta di RecyclerViewActions
che può essere utilizzata per scorrere fino alle posizioni o per eseguire azioni sugli elementi:
scrollTo()
: scorre fino alla vista corrispondente, se esistente.scrollToHolder()
: scorre fino al proprietario della visualizzazione corrispondente, se esistente.scrollToPosition()
: scorre fino a una posizione specifica.actionOnHolderItem()
: esegue un'azione di visualizzazione su un blocco della visualizzazione 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 includono alcuni esempi tratte dall'esempio RecyclerViewSample:
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 delle liste Espresso nei test Android, consulta le seguenti risorse.
Samples
- DataAdapterSample:
mostra il punto di ingresso
onData()
per Espresso, per elenchi eAdapterView
oggetti.