Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Listas de Espresso

Espresso ofrece mecanismos para desplazarse hacia un elemento específico o realizar una acción en él, para dos tipos de listas: vistas de adaptador y vistas de reciclador.

Cuando se trata de listas, especialmente aquellas creadas con un objeto RecyclerView o AdapterView, es posible que la vista que deseas no aparezca en la pantalla, porque solo se muestra y recicla una pequeña cantidad de elementos secundarios a medida que te desplazas. En este caso, no se puede usar el método scrollTo(), ya que requiere una vista existente.

Cómo interactuar con elementos de lista de la vista de adaptador

En lugar de usar el método onView(), inicia tu búsqueda con onData() y proporciona un buscador de coincidencias con los datos que admiten la vista que deseas hacer coincidir. Espresso hará todo el trabajo de encontrar la fila en el objeto Adapter y hacer 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, que está respaldado por un SimpleAdapter que incluye datos para cada fila de un objeto Map<String, Object>.

La actividad de lista que se muestra actualmente en la pantalla contiene una lista con 23 elementos. Cada uno de ellos tiene un número, almacenado como una string, asignado a un número diferente, que se guarda como un objeto.

Cada mapa tiene dos entradas: una clave "STR" que contiene una string, como "item: x", y una clave "LEN" que contiene un Integer, que representa la longitud 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)), 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 método is(instanceOf(Map.class)) reduce la búsqueda a cualquier elemento de 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 queremos hacer clic específicamente en un elemento, por lo que reducimos aún más la búsqueda de la siguiente manera:

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 largo y queremos reutilizarlo en otras ubicaciones, se debe escribir un buscador de coincidencias withItemContent personalizado 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 del tipo Map. Anula el método matchesSafely(), coloca el buscador de coincidencias que se encontró antes y haz que coincida con un Matcher<String> que puedas pasar como argumento. Esto te permite llamar a withItemContent(equalTo("foo")). Para abreviar el código, puedes crear otro buscador de coincidencias que ya llame a equalTo() y acepte 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, echa un vistazo al método testClickOnItem50() que está dentro de la clase AdapterViewTest y a este buscador de coincidencias LongListMatchers personalizado 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, quizá quieras hacer clic en la segunda columna de la fila de LongListActivity, que muestra la longitud de string del contenido en la primera columna:

En este ejemplo, sería útil extraer solo la longitud de un fragmento de contenido en particular. Este proceso implica determinar el valor de la segunda columna de una fila.

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.

Si quieres usar Espresso a fin de interactuar con RecyclerViews, puedes usar el paquete espresso-contrib, que tiene una colección de RecyclerViewActions disponibles para desplazarte a posiciones o realizar acciones en los elementos:

  • scrollTo(): se desplaza hasta la vista coincidente.
  • scrollToHolder(): se desplaza hasta el contenedor de la vista coincidente.
  • 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.

En los siguientes fragmentos, se muestran algunos ejemplos de RecyclerViewSample:

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 los siguientes recursos.

Muestras

  • DataAdapterSample: muestra el punto de entrada de onData() para Espresso, y para las listas y los objetos AdapterView.