A Visualização do desenvolvedor para Android 11 já está disponível. Teste e compartilhe seu feedback.

Listas do Espresso

O Espresso oferece mecanismos para rolar até um item específico ou agir nele para dois tipos de lista: visualizações do adaptador e visualizações de reciclagem.

Ao lidar com listas, especialmente aquelas criadas com um objeto RecyclerView ou AdapterView, a visualização do seu interesse pode nem aparecer na tela, porque apenas um pequeno número de elementos filhos é exibido e reciclado à medida que você rola a tela. O método scrollTo() não pode ser usado nesse caso, porque exige uma visualização existente.

Interagir com itens da lista de visualizações do adaptador

Em vez de usar o método onView(), inicie sua pesquisa com onData() e disponibilize um matcher nos dados que estão apoiando a visualização que você quer associar. O Espresso fará todo o trabalho para encontrar a linha no objeto Adapter e tornar o item visível na janela de visualização.

Associar dados usando um matcher de visualização personalizado

A atividade abaixo contém uma ListView, apoiada por um SimpleAdapter que contém dados para cada linha em um objeto Map<String, Object>.

A atividade de lista exibida na tela no momento contém uma lista com 23 itens. Cada item tem um número armazenado como uma string, mapeado para outro número, que é armazenado como um objeto.

Cada mapa tem duas entradas: uma chave "STR" que contém uma string, como "item: x", e uma chave "LEN" que contém um Integer, que representa o comprimento do conteúdo. Por exemplo:

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

O código para um clique na linha com "item: 50" fica assim:

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

Observe que o Espresso rola a lista automaticamente, conforme necessário.

Vamos separar o Matcher<Object> dentro de onData(). O método is(instanceOf(Map.class)) restringe a pesquisa a qualquer item da AdapterView apoiado por um objeto Map.

No nosso caso, esse aspecto da consulta corresponde a cada linha da visualização em lista, mas queremos clicar especificamente em um item para restringir ainda mais a pesquisa com:

Kotlin

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

Java

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

Este Matcher<String, Object> corresponderá a qualquer mapa que contenha uma entrada com a chave "STR" e o valor "item: 50". Como o código para essa pesquisa é longo e queremos reutilizá-lo em outros locais, vamos criar um matcher withItemContent personalizado:

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

Use um BoundedMatcher como base para corresponder apenas aos objetos do tipo Map. Modifique o método matchesSafely(), inserindo o matcher encontrado anteriormente e fazendo a correspondência com um Matcher<String> que você possa transmitir como argumento. Isso permite que você chame withItemContent(equalTo("foo")). Para abreviar o código, crie outro matcher que já chame o equalTo() e aceite um 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));
    }
    

Agora, o código para clicar no item é simples:

Kotlin

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

Java

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

Para ver o código completo desse teste, consulte o método testClickOnItem50() na classe AdapterViewTest e este matcher LongListMatchers personalizado (em inglês) no GitHub.

Associar uma visualização filha específica

O exemplo acima emite um clique no meio da linha inteira de uma ListView. E se quisermos realizar uma operação em um elemento filho específico da linha? Por exemplo, se quisermos clicar na segunda coluna da linha da LongListActivity, que exibe o comprimento da string do conteúdo na primeira coluna:

Nesse exemplo, seria útil extrair apenas o comprimento de uma parte específica do conteúdo. Esse processo envolve a determinação do valor da segunda coluna em uma linha.

Basta adicionar uma especificação onChildView() à implementação 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());
    

Interagir com itens da lista de visualizações de reciclagem

Como os objetos RecyclerView funcionam de maneira diferente dos objetos AdapterView, não é possível usar onData() para interagir com eles.

Para interagir com RecyclerViews usando o Espresso, use o pacote espresso-contrib, que tem um conjunto de RecyclerViewActions que podem ser usadas para rolar para posições ou realizar ações em itens:

  • scrollTo(): rola para a visualização correspondente.
  • scrollToHolder(): rola para o fixador da visualização correspondente.
  • scrollToPosition(): rola para uma posição específica.
  • actionOnHolderItem(): realiza uma ação de visualização em um fixador de visualização correspondente.
  • actionOnItem(): realiza uma ação de visualização em uma visualização correspondente.
  • actionOnItemAtPosition(): realiza uma ViewAction em uma visualização em uma posição específica.

Os seguintes snippets apresentam alguns exemplos da amostra 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()));
    }
    

Outros recursos

Para saber mais sobre como usar listas do Espresso em testes do Android, consulte os recursos a seguir.

Exemplos

  • DataAdapterSample (em inglês): mostra o ponto de entrada onData() do Espresso para listas e objetos AdapterView.