Introdução ao Espresso
O que é Espresso? 🤔
Espresso é uma estrutura de teste de instrumentação disponibilizada pelo Google para facilitar o teste da interface do usuário.
Por quê usar?
Acredito que a maioria dos desenvolvedores já fizeram algum tipo de teste nos seus aplicativos, sejam testes básicos num cenário de vida real, simplesmente para verificar se a soma de duas variáveis correspondem a um certo valor, ou se uma Activity está a comportar-se da forma como devia ao usarmos um Intent.
Escrever bons testes pode ser um desafio, e executar um teste confiável para interface de Usuário pode ser substancialmente mais difícil ainda.
Interfaces de Usuário são:
- Assíncronas
- Orientadas por eventos, transições e dados carregados por tarefas em segundo plano.
Codificar em torno disso sem qualquer ajuda de uma estrutura de teste de interface do usuário exigiria muito trabalho.
O Framework Espresso foi criado Justamente para este proposito, para permitir que desenvolvedores possam criar Testes de UI que são:
Concisos, Confiáveis e usando uma API fluente.
Suponhamos que eu lhe entregue um telemóvel com um aplicativo, para testar uma funcionalidade que acabo de implementar. como por exemplo verificar se a minha App envia mensagens de uma Activity para a outra de forma correcta.
O que você faria?
- Encontrar a View: Que permite inserir a mensagem.
- Executar uma acção: Neste caso seria escrever a mensagem.
- E por fim, Inspeccionar o Resultado: Verificando se a mensagem que foi escrita corresponde com a mensagem que foi enviada.
Isto é simples de entender para uma pessoa. Mas como codificariamos isto?🤷
Simples, basta-nos perceber os principais componentes do Espresso: Espresso, ViewMatchers, ViewActions, e ViewAssertions.
1. Espresso
Ponto de entrada para interacções com views(via onView()
e onData()
).
2. ViewMatchers
Uma colecção de objectos que implementam a interface Matcher<?superView>
. Você pode passar um ou mais destes para o método onView() para localizar uma visão dentro da hierarquia de visão actual.
3. ViewActions
Uma coleção de objetos ViewAction que podem ser passados para o método ViewInteraction.perform()
, como por exemplo: click().
4. ViewAssertions
Uma coleção de objetos ViewAssertion que podem ser passados pelo método ViewInteraction.check()
. Na maioria das vezes, usaremos o Assertion “matches”, com um ViewMatcher, para confirmar o estado da exibição de uma view.
O fluxo de teste que usamos anteriormente não se difere da anatomia do Espresso:
- Primeiro encontramos a view com
onView(Matcher<View>)
- De seguida executamos uma accao usando
.perform(ViewAction)
- E por fim verificamos o resultado com
.check(ViewAssertion)
Agora vamos a um exemplo prático👨💻
Para o exemplo que preparei, criei um aplicativo simples que envia uma mensagem de uma activity para a outra. Vamos implementar alguns métodos para testar o aplicativo com o Espresso. O aplicativo pode ser baixado no seguinte LINK.
Parte 1
Após termos baixado o aplicativo e importado para o nosso Android Studio, ja podemos comecar.
- Adicione as seguintes dependencias ao
build.gradle
da app:
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
2. Adicione tambem a linha de codigo abaixo e sincronize o programa.
defaultConfig{
testInstrumentationRunner “android.support.test.runner.AndroidJUnitRunner”
}
O AndroidJUnitRunner é o ponto de entrada para executar todos os nossos testes. Ele controla o ambiente de teste, o apk e inicia todos os testes definidos no nosso pacote de testes.
Parte 2
Os testes de Instrumentação ficam na pasta (androidTest)
. Então,
- Primeiro vamos criar uma classe onde faremos os nossos testes, podemos chamar de
EspressoUITest
.
@RunWith(AndroidJUnit4.class)
public class EspressoUITest {
@Rule
public ActivityTestRule mActivityRule = new ActivityTestRule<> (MainActivity.class);}
Até aqui já temos duas Annotations: @RunWith
e @Rule.
O @RunWith
serve para criar classes de testes de instrumentação com JUnit4.
O @Rule
permite-nos alterar o comportamento de cada método de teste de forma reusável. o teste supracitado usa ActivityTestRule
(providencia métodos para testar uma unica activity, neste caso a MainActivity).
Parte 3
Agora vamos começar com o teste, vamos criar um método void chamado testarActivities
onde iremos testar se as views aparecem, após clicarmos os botões Enviar
.
@Test
public void testarActivities(){
onView(withId(R.id.enviar)).perform(click());
onView(withId(R.id.titulo1)).check(matches(isDisplayed()));
onView(withId(R.id.enviar1)).perform(click());
onView(withId(R.id.titulo)).check(matches(isDisplayed()));
}
Não podemos esquecer-nos da Annotation @Test
, pois ela é que diz ao JUnit que o método em questão é para testes.
Na primeira linha, fizemos uma combinação de um ViewMatcher (usado para encontrar a view onde vamos efectuar a acção) e um ViewAction (usado para efectuar a acção que neste caso foi o click do botão).
onView(withId(R.id.enviar)).perform(click());
Na segunda linha, fizemos uma combinação de um ViewMatcher (usado para encontrar a view onde vamos efectuar a acção neste caso o titulo da 2ª Activity) e um ViewAssertion(usado para verificar se o titulo descrito no matcher aparece na 2ª Activity).
onView(withId(R.id.titulo1)).check(matches(isDisplayed()));
Nas linhas seguintes efectuamos o mesmo processo para testar se o titulo da Activity1 aparecerá ao efectuarmos o click do botão enviar na Activity2.
onView(withId(R.id.enviar1)).perform(click());
onView(withId(R.id.titulo)).check(matches(isDisplayed()));
Para corrermos o teste simplesmente clicamos com botão direito na Classe EspressoUITest
, clicamos em Run ‘EspressoUITest’ e de seguida escolhemos onde fazer o teste, pois este pode ser feito no emulador ou no nosso dispositivo.
Quando o teste começar observe o dispositivo a executar o código. clicando o botão na primeira activity, de seguida clicando o botão da 2ª activity automaticamente.
Se tudo tiver corrido bem veremos um: All Tests passed no canto inferior esquerdo, indicando que os testes foram efectuados com sucesso.
Caso contrário, veremos um circulo vermelho, e erros a espera de serem corrigidos.
Parte 4
Chegamos a 4ª e ultima parte deste artigo, onde iremos testar se as mensagens escritas pelo usuário na 1ª Activity correspondem a mensagem recebida na 2ª Activity, vamos criar um outro método void chamado testeInputOutput
, e usaremos a mesma lógica.
@Test
public void testeInputOutput(){
onView(withId(R.id.mensagem)).perform(typeText("I've heard that Rosario is Batman"));
onView(withId(R.id.enviar)).perform(click());
onView(withId(R.id.mensagem_recebida1)).check(matches(withText("I've heard that Rosario is Batman")));
}
Na primeira operação, fizemos uma combinação de um ViewMatcher (usado para encontrar a view onde vamos efectuar a acção) e um ViewAction (usado para efectuar a acção que neste caso foi inserir texto).
onView(withId(R.id.mensagem)).perform(typeText("I've heard that Rosario is Batman"));
Na segunda operação, fizemos uma combinação de um ViewMatcher (usado para encontrar a view onde vamos efectuar a acção) e um ViewAction (usado para efectuar a acção que neste caso foi efectuar um click).
onView(withId(R.id.enviar)).perform(click());
Por fim, fizemos a combinação de um ViewMatcher (usado para encontrar a view onde vamos efectuar a acção) e um ViewAssertion(usado para verificar se o texto inserido na 1a Activity corresponde ao texto recebido na 2ª activity).
onView(withId(R.id.mensagem_recebida1)).check(matches(withText("I've heard that Rosario is Batman")));
Corra somente o métodotesteInputOutput
e veja o programa a ser executado automaticamente.
Nosso Código Completo deve estar assim: