O Espresso oferece mecanismos para rolar a tela ou realizar ações em um item específico para dois tipos de listas: visualizações de adaptador e visualizações de reciclagem.
Ao lidar com listas, especialmente aquelas criadas com uma RecyclerView
ou uma
AdapterView
, a visualização do seu interesse pode nem estar
na tela porque apenas um pequeno número de filhos é exibido
recicladas conforme você rola a tela. O método scrollTo()
não pode ser usado nesse caso
porque ela exige uma visualização existente.
Interagir com itens da lista de visualizações do adaptador
Em vez de usar o método onView()
, comece sua pesquisa com onData()
e
forneça uma correspondência com os dados que apoiam a visualização que você gostaria de associar.
O Espresso fará todo o trabalho para encontrar a linha no objeto Adapter
e
tornando 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
do conteúdo. Exemplo:
{"STR" : "item: 0", "LEN": 7}
O código para um clique na linha com "item: 50" fica assim:
onData(allOf(`is`(instanceOf(Map::class.java)), hasEntry(equalTo("STR"),
`is`("item: 50")))).perform(click())
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()
. A
O método is(instanceOf(Map.class))
restringe a pesquisa a qualquer item do
AdapterView
, que é apoiado por um objeto Map
.
Em nosso caso, esse aspecto da consulta corresponde a cada linha da visualização em lista, mas desejar clicar especificamente em um item, restringimos ainda mais a pesquisa com:
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 pesquisar isso é
e quiser reutilizá-lo em outros locais, vamos criar uma
withItemContent
para isso:
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)
}
}
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 objetos do tipo
Map
. Substitua o método matchesSafely()
, colocando o matcher encontrado
anteriormente e compará-lo com um Matcher<String>
que você pode transmitir como um
. Isso permite que você chame withItemContent(equalTo("foo"))
. Para código
breve, crie outro matcher que já chame os métodos equalTo()
e
aceita um objeto String
:
fun withItemContent(expectedText: String): Matcher<Object> {
checkNotNull(expectedText)
return withItemContent(equalTo(expectedText))
}
public static Matcher<Object> withItemContent(String expectedText) {
checkNotNull(expectedText);
return withItemContent(equalTo(expectedText));
}
Agora, o código para clicar no item é simples:
onData(withItemContent("item: 50")).perform(click())
onData(withItemContent("item: 50")).perform(click());
Para ver o código completo desse teste, consulte o método testClickOnItem50()
.
no
AdapterViewTest
classe e
este LongListMatchers
personalizado
matcher 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, nós
quiser 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 do
DataInteraction
:
onData(withItemContent("item: 60"))
.onChildView(withId(R.id.item_size))
.perform(click())
onData(withItemContent("item: 60"))
.onChildView(withId(R.id.item_size))
.perform(click());
Interagir com itens da lista de visualizações de reciclagem
Os objetos RecyclerView
funcionam de maneira diferente dos objetos AdapterView
. Portanto,
Não é possível usar onData()
para interagir com eles.
Para interagir com RecyclerViews usando o Espresso, você pode usar o
espresso-contrib
, que tem uma coleção de
RecyclerViewActions
que podem ser usadas 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 RecyclerViewSample (em inglês) amostra:
@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"))
)
)
}
@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"))
));
}
@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()))
}
@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()));
}
@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()))
}
@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 mais informações sobre como usar listas do Espresso em testes do Android, consulte o recursos a seguir.
Amostras
- DataAdapterSample (link em inglês):
Mostra o ponto de entrada
onData()
do Espresso, para listas eAdapterView
objetos.