Este documento explica como concluir tarefas de teste automatizadas comuns usando o API do Espresso.
A API Espresso incentiva os autores de testes a pensar do que um usuário pode
ao interagir com o aplicativo: localizar elementos da interface e interagir
com eles. Ao mesmo tempo, o framework impede o acesso direto a atividades
e visualizações do aplicativo, porque se retém esses objetos e opera
fora da linha de execução de IU é uma das principais fontes de inconsistência nos testes. Assim, você
não consultar métodos como getView()
e getCurrentActivity()
na API do Espresso.
Você ainda pode operar em visualizações com segurança, implementando suas próprias subclasses de
ViewAction
e ViewAssertion
.
Componentes da API
Os principais componentes do Espresso incluem:
- Espresso: ponto de entrada para interações com visualizações (via
onView()
eonData()
). Também expõe APIs que não estão necessariamente vinculadas a nenhuma visualização, como comopressBack()
. - ViewMatchers: uma coleção de objetos que implementam a
interface
Matcher<? super View>
. Você pode passar um ou mais deles para o MétodoonView()
para localizar uma visualização na hierarquia atual. - ViewActions: uma coleção de objetos
ViewAction
que podem ser transmitidos para o métodoViewInteraction.perform()
, comoclick()
. - ViewAssertions: uma coleção de objetos
ViewAssertion
que podem ser passou o métodoViewInteraction.check()
. Na maioria das vezes, você usará o corresponde à declaração, que usa um matcher de visualização para declarar o estado da visualização selecionada no momento.
Exemplo:
Kotlin
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()))
Java
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()));
Encontrar uma visualização
Na grande maioria dos casos, o método onView()
usa um matcher de Hamcrest
que deve corresponder a uma (e somente uma) visualização na visualização atual.
hierarquia. Quem já usou os matchers é muito conhecido, e eles já estão acostumados.
com o Mockito ou o JUnit. Se você não conhece os correspondentes de Hamcrest,
sugerimos que você comece com uma rápida análise neste
apresentação.
Em geral, a visualização desejada tem um R.id
exclusivo, e um matcher withId
simples
restringir a pesquisa de visualizações. No entanto, existem muitos casos legítimos
não foi possível determinar R.id
no momento do desenvolvimento do teste. Por exemplo, a visualização específica
pode não ter um R.id
ou o R.id
não é exclusivo. Isso pode fazer com que as
de instrumentação são frágeis e complicados de escrever, porque a maneira normal de
acessar a visualização (com findViewById()
) não funciona. Assim, é possível
precisar acessar membros privados da atividade ou fragmento que contém a visualização ou
encontre um contêiner com um R.id
conhecido e navegue até o conteúdo dele para a
visualização específica.
O Espresso lida com esse problema de forma limpa, permitindo restringir a visualização.
usando objetos ViewMatcher
atuais ou seus próprios objetos personalizados.
Encontrar uma visualização pelo R.id
é tão simples quanto chamar onView()
:
Kotlin
onView(withId(R.id.my_view))
Java
onView(withId(R.id.my_view));
Às vezes, os valores R.id
são compartilhados entre várias visualizações. Quando isso acontece, um
tentar usar um determinado R.id
gera uma exceção, como
AmbiguousViewMatcherException
. A mensagem de exceção fornece uma
representação da hierarquia de visualizações atual, que pode ser pesquisada e encontrada
as visualizações que correspondem ao R.id
não exclusivo:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
Analisando os diversos atributos das visualizações, você pode encontrar resultados únicos
propriedades identificáveis. No exemplo acima, uma das visualizações tem o texto
"Hello!"
: Você pode usar isto para restringir sua pesquisa usando uma combinação
correspondentes:
Kotlin
onView(allOf(withId(R.id.my_view), withText("Hello!")))
Java
onView(allOf(withId(R.id.my_view), withText("Hello!")));
Você também pode optar por não reverter nenhum dos matchers:
Kotlin
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
Java
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))));
Consulte ViewMatchers
.
para os matchers de visualização fornecidos pelo Espresso.
Considerações
- Em um aplicativo com comportamento adequado, todas as visualizações com as quais um usuário pode interagir
deve conter texto descritivo ou uma descrição do conteúdo. Consulte
Como tornar apps mais acessíveis para mais
detalhes. Se você não conseguir restringir uma pesquisa usando
withText()
ouwithContentDescription()
, considere tratá-lo como um bug de acessibilidade. - Use o matcher menos descritivo que encontrar a visualização que você está procurando
pelas quais Não especifique demais, porque isso forçará a estrutura a realizar mais trabalho do que
é necessário. Por exemplo, se uma visualização puder ser identificada exclusivamente por seu texto, você
não é necessário especificar que a visualização também pode ser atribuída a partir de
TextView
. Para muitos visualizações, oR.id
da visualização deve ser suficiente. - Se a visualização de destino estiver dentro de uma
AdapterView
, comoListView
,GridView
ouSpinner
, o métodoonView()
pode não funcionar. Nessas casos, useonData()
.
Realizar uma ação em uma visualização
Quando você encontra um matcher adequado para a visualização de destino, é possível
executar instâncias de ViewAction
nela usando o método de execução.
Por exemplo, para clicar na visualização:
Kotlin
onView(...).perform(click())
Java
onView(...).perform(click());
Você pode executar uma ou mais ações com uma chamada de execução:
Kotlin
onView(...).perform(typeText("Hello"), click())
Java
onView(...).perform(typeText("Hello"), click());
Se a visualização com que você está trabalhando está dentro de um ScrollView
(vertical ou
horizontal), considere as ações anteriores que exigem que a visualização seja
mostrado, como click()
e typeText()
, com scrollTo()
. Isso
garante que a visualização seja exibida antes de prosseguir para a outra ação:
Kotlin
onView(...).perform(scrollTo(), click())
Java
onView(...).perform(scrollTo(), click());
Consulte ViewActions
.
para as ações de visualização fornecidas pelo Espresso.
Verificar as declarações de visualização
As declarações podem ser aplicadas à visualização selecionada no momento com o check()
. A declaração mais usada é a matches()
. Ele usa uma
Objeto ViewMatcher
para declarar o estado da visualização selecionada no momento.
Por exemplo, para verificar se uma visualização tem o texto "Hello!"
:
Kotlin
onView(...).check(matches(withText("Hello!")))
Java
onView(...).check(matches(withText("Hello!")));
Se quiser declarar que "Hello!"
é o conteúdo da visualização, a seguinte prática é recomendada:
Kotlin
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))
Java
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));
Por outro lado, se você quiser declarar que uma visualização com o texto "Hello!"
é
visível, por exemplo, após uma alteração da sinalização de visibilidade das visualizações, o
o código está correto.
Teste simples para declaração de visualizações
Neste exemplo, SimpleActivity
contém um Button
e um TextView
. Quando o
for clicado, o conteúdo de TextView
mudará para "Hello Espresso!"
.
Veja como testar isso com o Espresso:
Clicar no botão
O primeiro passo é procurar uma propriedade que ajude a encontrar o botão. A
na SimpleActivity
tem um R.id
exclusivo, conforme esperado.
Kotlin
onView(withId(R.id.button_simple))
Java
onView(withId(R.id.button_simple));
Agora, para executar o clique:
Kotlin
onView(withId(R.id.button_simple)).perform(click())
Java
onView(withId(R.id.button_simple)).perform(click());
Verificar o texto de TextView
O TextView
com o texto a ser verificado também tem um R.id
exclusivo:
Kotlin
onView(withId(R.id.text_simple))
Java
onView(withId(R.id.text_simple));
Agora, para verificar o texto do conteúdo:
Kotlin
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")))
Java
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));
Verificar o carregamento de dados nas visualizações do adaptador
O AdapterView
é um tipo especial de widget que carrega os dados dinamicamente a partir de
com um adaptador. O exemplo mais comum de um AdapterView
é ListView
. Conforme
em oposição a widgets estáticos, como LinearLayout
, apenas um subconjunto
AdapterView
filhos podem ser carregados na hierarquia de visualização atual. Um simples
A pesquisa onView()
não encontrou visualizações que não estejam carregadas no momento.
O Espresso lida com isso fornecendo um ponto de entrada onData()
separado, que é
carregar primeiro o item do adaptador em questão, colocando-o em foco antes de
que opera nele ou em qualquer um dos filhos dele.
Aviso:implementações personalizadas de
AdapterView
pode ter problemas com o onData()
.
se quebrarem contratos de herança, especialmente os
API getItem()
. Nesses casos, a melhor providência é
refatorar o código do aplicativo. Se não puder fazer isso, você pode implementar um
AdapterViewProtocol
personalizado correspondente. Para mais informações, confira
observe o padrão
AdapterViewProtocols
fornecida pelo Espresso.
Teste simples para visualização do adaptador
Este teste simples demonstra como usar onData()
. SimpleActivity
contém um
Spinner
com alguns itens que representam tipos de bebidas de café. Quando um
item for selecionado, há um TextView
que muda para "One %s a day!"
, em que
%s
representa o item selecionado.
O objetivo desse teste é abrir a Spinner
, selecionar um item específico e
verificar se TextView
contém o item. Como a classe Spinner
é baseada
em AdapterView
, recomendamos usar onData()
em vez de onView()
para
que correspondem ao item.
Abrir a seleção do item
Kotlin
onView(withId(R.id.spinner_simple)).perform(click())
Java
onView(withId(R.id.spinner_simple)).perform(click());
Selecionar um item
Para a seleção do item, o Spinner
cria um ListView
com o próprio conteúdo.
Essa visualização pode ser muito longa, e talvez o elemento não seja contribuído para a visualização
hierarquia. Usando onData()
, forçamos o elemento desejado a aparecer na visualização.
hierarquia. Os itens no Spinner
são strings, então queremos associar um item
que é igual à string "Americano"
:
Kotlin
onData(allOf(`is`(instanceOf(String::class.java)), `is`("Americano"))).perform(click())
Java
onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());
Verificar se o texto está correto
Kotlin
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))))
Java
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))));
Depurar
O Espresso oferece informações úteis sobre depuração quando um teste falha:
Gerar registros
O Expresso registra todas as ações de visualização no Logcat. Por exemplo:
ViewInteraction: Performing 'single click' action on view with text: Espresso
Hierarquia de visualização
O Espresso imprime a hierarquia de visualizações na mensagem de exceção quando onView()
falhar.
- Se o
onView()
não encontrar a visualização de destino, umNoMatchingViewException
será gerada. É possível examinar a hierarquia de visualizações na string da exceção para analisar por que o matcher não associou nenhuma visualização. - Se
onView()
encontrar várias visualizações que correspondam ao matcher especificado, umaAmbiguousViewMatcherException
é gerada. A hierarquia de visualizações é impressa, visualizações correspondentes são marcadas com o rótuloMATCHES
:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
Ao lidar com uma hierarquia de visualizações complicada ou um comportamento inesperado dos widgets é sempre útil usar Hierarchy Viewer no Android Studio para uma explicação.
Avisos de visualização do adaptador
O Espresso avisa os usuários sobre a presença de widgets AdapterView
. Quando um onView()
uma operação gera um NoMatchingViewException
e widgets AdapterView
são
presentes na hierarquia de visualizações, a solução mais comum é usar onData()
.
A mensagem da exceção incluirá um aviso com uma lista das visualizações do adaptador.
Você pode usar essas informações para invocar onData()
para carregar a visualização de destino.
Outros recursos
Para mais informações sobre o uso do Espresso em testes do Android, consulte o recursos a seguir.
Amostras
- CustomMatcherSample:
Mostra como estender o Espresso para associar a propriedade hint de um objeto
EditText
. - RecyclerViewSample (em inglês):
RecyclerView
para o Espresso. - Outros recursos para testes