O Espresso oferece mecanismos para rolar ou realizar ações em um determinado item em dois tipos de listas: 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 estar
na tela, porque apenas um pequeno número de filhos é exibido e é
recicado conforme você rola a tela. O método scrollTo()
não pode ser usado nesse caso
porque exige uma visualização já 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
forneça um correspondente aos 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 um ListView
, que é apoiado por um SimpleAdapter
que contém dados para cada linha em um objeto Map<String, Object>
.
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.java)), 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 Matcher<Object>
dentro de onData()
. O
método is(instanceOf(Map.class))
restringe a pesquisa a qualquer item da
AdapterView
, que tem como base um objeto Map
.
No nosso caso, esse aspecto da consulta corresponde a cada linha da visualização em lista, mas como queremos clicar especificamente em um item, restringimos ainda mais a pesquisa com:
Kotlin
hasEntry(equalTo("STR"), `is`("item: 50"))
Java
hasEntry(equalTo("STR"), is("item: 50"))
Esse 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 para isso:
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 a objetos do tipo
Map
. Substitua o método matchesSafely()
, inserindo o correspondente encontrado
anteriormente e fazendo a correspondência com um Matcher<String>
que você pode transmitir como
argumento. Isso permite que você chame withItemContent(equalTo("foo"))
. Para simplificar o código, você pode criar 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 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, gostaríamos
de clicar na segunda coluna da linha de LongListActivity
,
que exibe o String.length do conteúdo na primeira coluna:
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 forma diferente dos objetos AdapterView
, onData()
não pode ser usado para interagir com eles.
Para interagir com RecyclerViews usando o Espresso, use o
pacote espresso-contrib
, que tem uma coleção de
RecyclerViewActions
que pode ser usada para rolar para posições ou realizar ações em itens:
scrollTo()
: rola para a visualização correspondente, se houver.scrollToHolder()
: rola para o fixador da visualização correspondente, se houver.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 snippets a seguir apresentam alguns exemplos do exemplo 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())); }
Outros recursos
Para saber mais sobre o uso de listas do Espresso em testes do Android, consulte os recursos abaixo.
Exemplos
- DataAdapterSample (link em inglês):
mostra o ponto de entrada
onData()
do Espresso, para listas e objetosAdapterView
.