Espresso ofrece mecanismos para desplazarse o actuar sobre un elemento en particular para dos tipos de listas: vistas de adaptadores y vistas de reciclador.
Cuando trabajes con listas, especialmente las creadas con un RecyclerView
o un
AdapterView
, es posible que la vista que te interesa ni siquiera esté en
la pantalla porque solo se muestra un pequeño número de hijos y son
reciclados a medida que te desplazas. No se puede usar el método scrollTo()
en este caso.
porque requiere una vista existente.
Cómo interactuar con elementos de lista de la vista de adaptador
En lugar de usar el método onView()
, comienza tu búsqueda con onData()
y
proporcionar un comparador con los datos que respaldan la vista que deseas.
Espresso encontrará la fila en el objeto Adapter
y
para que el elemento sea visible en el viewport.
Cómo hacer coincidir los datos mediante un buscador de coincidencias de vistas personalizado
La siguiente actividad contiene un ListView
, respaldado por un SimpleAdapter
.
que contiene datos para cada fila en un objeto Map<String, Object>
.
Cada mapa tiene dos entradas: una clave "STR"
que contiene una cadena, como
"item: x"
y un "LEN"
clave que contiene un Integer
, que representa la
la duración del contenido. Por ejemplo:
{"STR" : "item: 0", "LEN": 7}
Este es el código para hacer clic en la fila con "item: 50":
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());
Ten en cuenta que Espresso se desplaza por la lista automáticamente según sea necesario.
Desmontemos el Matcher<Object>
dentro de onData()
. El
El método is(instanceOf(Map.class))
reduce la búsqueda a cualquier elemento de la
AdapterView
, que está respaldado por un objeto Map
En nuestro caso, este aspecto de la consulta coincide con cada fila de la vista de lista, pero hacer clic específicamente en un elemento, por lo que delimitamos aún más la búsqueda con lo siguiente:
Kotlin
hasEntry(equalTo("STR"), `is`("item: 50"))
Java
hasEntry(equalTo("STR"), is("item: 50"))
Este Matcher<String, Object>
coincidirá con cualquier mapa que contenga una entrada con
la clave "STR"
y el valor "item: 50"
. Debido a que el código para buscar esto es
por mucho tiempo y queremos reutilizarlo en otras ubicaciones, escribamos una
Comparador de withItemContent
para eso:
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); } };
Usas un BoundedMatcher
como base porque solo coincide con objetos de tipo.
Map
Anula el método matchesSafely()
y coloca el comparador encontrado.
antes y la compara con una Matcher<String>
que puedes pasar como
argumento. Esto te permite llamar a withItemContent(equalTo("foo"))
. Para código
brevedad, puedes crear otro comparador que ya llame a equalTo()
y
acepta un objeto 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)); }
El código para hacer clic en el elemento es simple:
Kotlin
onData(withItemContent("item: 50")).perform(click())
Java
onData(withItemContent("item: 50")).perform(click());
Para obtener el código completo de esta prueba, consulta el método testClickOnItem50()
.
en el
AdapterViewTest
clase y
esta LongListMatchers
personalizada
de búsqueda en GitHub.
Cómo hacer coincidir una vista secundaria específica
En el ejemplo anterior, se emite un clic en el medio de toda la fila de una ListView
.
¿Qué ocurre si deseas realizar operaciones en un elemento secundario específico de la fila? Por ejemplo,
si quieres hacer clic en la segunda columna de la fila LongListActivity
,
que muestra la longitud de cadena del contenido en la primera columna:
Solo agrega una especificación de onChildView()
a tu implementación de
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());
Cómo interactuar con los elementos de la lista de la vista de reciclador
Los objetos RecyclerView
funcionan de manera diferente que los objetos AdapterView
, por lo que
No se puede usar onData()
para interactuar con ellos.
Para interactuar con RecyclerViews usando Espresso, puedes usar la
espresso-contrib
, que tiene una colección de
RecyclerViewActions
que se puede utilizar para desplazarse a posiciones o realizar acciones sobre los elementos:
scrollTo()
: se desplaza hasta la vista coincidente, si existe.scrollToHolder()
: Se desplaza hasta el contenedor de vistas coincidente (si existe).scrollToPosition()
: se desplaza hasta una posición específica.actionOnHolderItem()
: realiza una acción de vista en un titular de vista coincidente.actionOnItem()
: realiza una acción de vista en una vista coincidente.actionOnItemAtPosition()
: realiza una acción de vista en la vista de una posición específica.
Los siguientes fragmentos incluyen algunos ejemplos del RecyclerViewSample muestra:
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())); }
Recursos adicionales
Para obtener más información sobre el uso de listas de Espresso en pruebas de Android, consulta el los siguientes recursos.
Ejemplos
- DataAdapterSample:
Presenta el punto de entrada
onData()
para Espresso, para las listas y losAdapterView
objetos.