Concrete Logo
Hamburger button

Testes no Android com Espresso - parte 2

  • Blog
  • 6 de Setembro de 2016

Este post foi originalmente publicado no Medium pessoal do autor. Confira aqui.

Na primeira desta série, vimos como configurar nosso projeto. Se você quiser começar a partir desta parte, clone o projeto EspressoTests no github e faça um fork da branch ‘part_1’, que representa o estado do projeto ao final da parte 1. Hoje, vamos falar sobre asserções simples e interações com views.

Criando o primeiro teste

Vamos começar testando a tela de login. Para isso, crie uma classe ‘LoginActivityTest’ no diretório ‘/app/src/androidTest/’ do projeto, conforme a imagem abaixo:

Anote a classe que criamos com a anotação:

Isso indica que esta classe deve ser executada com o AndroidJUnitRunner. Se quiser saber mais sobre ele, acesse o link.

Agora, vamos definir nossa ActivityTestRule com a activity que será iniciada antes de todos os nossos testes, ou seja, a activity que queremos testar.

Os parâmetros do construtor são:

  • A activity que será testada (LoginActivity.class),
  • initialTouchMode (false),
  • Se deve iniciar automaticamente ou não (true).

Seu código deve estar desta maneira:

Escrevendo o primeiro teste

Agora vamos escrever nosso primeiro teste. Devemos testar os diferentes estados que a tela em questão pode assumir. Por exemplo, assim que iniciamos a LoginActivity, ela exibe uma imagem, dois campos de texto e um botão. Este pode ser considerado o estado inicial da tela. Vamos escrever um teste para verificar esse estado.

Uma das vantagens do Espresso é que a sintaxe é bem intuitiva. Praticamente podemos ler em linguagem natural o que o teste está executando. Vamos analisar a primeira linha do teste:

Poderíamos ler esta linha em linguagem natural, por exemplo: “Verifique que a view com id login_image está visível na tela”. Agora, explicando um pouco o que está acontecendo:

  • onView() vai receber o ViewMatchers que passarmos como parâmetro e irá nos retornar um objeto ViewInteraction. Em outras palavras, estamos passando pra ele a view que queremos interagir;
  • withId() vai receber o id da view e irá nos retornar um ViewMatchers;
  • check() vai receber um ViewAssertion. Ou seja, vai verificar se é válida a asserção que estamos passando como parâmetro;
  • matches() vai receber um ViewMatchers e retornar um ViewAssertion;
  • isDisplayed() é o ViewMatchers que utilizaremos.

Para saber mais sobre os objetos, clique nos links sobre ViewMatchersViewInteraction e ViewAssertion.

Executando o teste

Agora é só executar o teste certo? Errado! Precisamos desativar as animações nas opções de desenvolvedor do nosso emulador/device. Para fazer isso, vá em Configurações > Programador (ou opções de desenvolvedor) e desative estas três opções:

  • Animação em escala;
  • Escala de transição;
  • Escala de duração da animação.

O Espresso aguarda a UI Thread ficar ociosa (idle) para executar o próximo passo do teste. Porém, se as animações estiverem ligadas, ele irá se perder e os testes irão quebrar. Leia mais sobre isso aqui.

Agora, para rodar este teste, basta clicar no botão direito do mouse sobre o método e depois clicar na opção Run ‘whenActivityIsLaunched…’. Se tudo correr bem, você deve ver o teste ser executado no seu emulador e o console de testes estará assim:

Certo, mas como eu garanto que o Espresso está realmente funcionando e olhando se minhas views estão aparecendo na tela? Para garantir isso, vamos alterar a primeira linha deste teste que fizemos:

 

O método not() irá inverter o resultado que esperávamos. Então, estou dizendo neste teste que a view com id login_image não estará visível na tela, o que não é verdade, pois ao iniciar a tela, a imagem estará visível. Então o teste deve obrigatoriamente falhar. Se rodarmos o teste novamente:

Ótimo, nosso teste falhou, o que significa que o Espresso está fazendo as asserções corretamente. Melhor que isso, repare no log que ele mostra:

Ou seja, ele também nos mostra onde o teste está falhando. Agora podemos remover o método not() que adicionamos e rodar esse teste novamente. Deu verde? Então vamos em frente.

Este teste foi fácil, o que devemos fazer agora é testar os outros estados da tela. Um destes outros estados é quando deixamos um dos campos de texto em branco, e clicamos no botão de login. Quando isso acontece, o app exibe um dialog na tela. Vamos escrever um teste para verificar esse estado.

Reparem que utilizamos três novos métodos:

  • perform(): vai executar a ViewAction que receber como parâmetro;
  • typeText(): ViewAction para digitar um texto na view;
  • click(): ViewAction para clicar na view;
  • withText(): vai procurar a view que contenha o texto passado como parâmetro.

O primeiro bloco do nosso teste está executando as seguintes ações:

  • Escrevendo um texto no campo login_username;
  • Clicando no botão de login;
  • Verificando que o dialog está aparecendo (pois deixamos o campo senha em branco);
  • Clicando no Ok do dialog para fechá-lo.

O segundo bloco faz basicamente a mesma coisa que o primeiro bloco, só que com o campo login_password. A única diferença é que ele limpa o campo login_username antes, pois o campo está preenchido por causa do bloco anterior. Isso não é muito legal, ter que limpar o que os passos anteriores do teste fizeram para executar os próximos passos. Nossos testes devem validar os cenários de maneira isolada. Então, vamos refatorar este código.

Ótimo, agora temos dois testes, e um não está influenciando no outro. Porém ainda repetimos os mesmos passos para ambos os testes. Acho que podemos refatorá-los novamente.

Agora está melhor, temos um código mais limpo e reutilizável. Este é um bom momento para eu dar a primeira dica desta série:

Faça testes pequenos, não faça testes longos.

Evite fazer testes que executem várias coisas, foque em cenários pequenos e, de preferência, independentes.

Tente rodar os testes agora. Passou? Na minha tentativa não passou, e já vou explicar o motivo. Vamos analisar o log primeiro:

Aparentemente o Espresso não conseguiu efetuar o click no botão de login, ou seja, o problema está na segunda linha do método testEmptyFieldState:

Coloque um breakpoint nesta linha e vamos ver a tela do emulador neste momento:

 

Repare que o teclado virtual está ocupando uma boa parte da tela, inclusive ele está cobrindo o nosso botão de login, por isso o Espresso não pode clicar no botão. Para corrigir este problema vamos utilizar o método closeSoftKeyboard().

Este método irá fechar o teclado virtual após digitarmos o texto “defaultText”. Nosso botão de login ficará visível e o Espresso conseguirá clicar nele. Rode o teste novamente, acredito que agora vai passar.

Aí vai a segunda dica:

Sempre que tiver um teste que envolva digitar texto em um EditText, não se esqueça de usar o método closeSoftKeyboard() para esconder o telcado virtual.

Se você deixar o teclado virtual aberto durante os testes, ele vai ficar por cima de views que poder ser essenciais para seu teste, e ele irá falhar.

Ufa, bastante coisa até aqui, mas ainda temos dois cenários para validar:

  1. Quando os dois campos estão vazios, deve exibir o dialog;
  2. Quando os dois campos são preenchidos, deve abrir a MainActivity.

O cenário 1 vou deixar como exercício para você resolver. O cenário 2 envolve outro conceito que vou deixar para o próximo post. Ao final desta etapa, o seu código deve estar parecido com o branch ‘part_2’.

Se tiver dúvidas, sugestões ou encontrou alguma informação errada neste post, deixe um comentário abaixo. E na semana que vem eu volto com mais um post para essa série. Até lá!