Explorando a pirâmide de testes no Android — Testes instrumentados — parte 3

Na primeira e segunda partes dos posts sobre pirâmide de testes, tivemos uma visão geral dos conceitos básicos e de como está estruturada a base da pirâmide. Nesse post vamos explorar o centro dela, vamos falar especificamente dos testes instrumentados do seu projeto Android.

Por definição, testes instrumentados no Android são aqueles que rodam em aparelhos físicos ou em emuladores. Nesses testes o uso de mocks é reduzido e ganhamos a vantagem de utilizar a implementação real do framework do Android. Dessa forma seus teste vão poder fazer uso de SharedPreferences, Context, Parcelable e outras classes.

Eu costumo dividir o centro da pirâmide em 4 categorias (da base ao topo):

  • Testes com a nova versão 4.0 do Robolectric
  • Testes exclusivamente com AndroidJUnitRunner
  • Testes de UI com Espresso
  • Testes end-to-end com UI Automator ou outras ferramentas
Foto de uma pirâmide de testes com highlight no centro

Testes com a nova API do Robolectric

Como falado no post anterior, recentemente foi anunciado que o Robolectric vai fazer parte do framework oficial de testes do Android e que seus testes também vão poder ser construídos do pacote androidTest. Tudo indica que a partir da versão 4.0 do Robolectric em diante provavelmente vamos necessitar de emuladores ou devices para rodar esse tipo de teste. Além disso a forma de construir os testes vai ser padronizada para ficar muito semelhante ao Espresso (Vamos falar sobre ele nos próximos tópicos).

Abaixo temos um exemplo de teste com Robolectric na nova versão 4.0:

Exemplo de teste instrumentado com Robolectric. Retirado de: http://robolectric.org/blog/2018/05/09/robolectric-4-0-alpha/

Ainda não se tem muita informação sobre esse tema, já que o anúncio foi feito recentemente e não existe muita documentação disponível. Atualizarei esse post assim que tiver mais novidades!

Para mais informações acessar esse link.

Testes apenas com AndroidJUnitRunner

O AndroidJUnitRunner é um test runner que permite com que testes com JUnit sejam executados sobre aparelhos Android. Nesses testes nós podemos ter acesso a algumas classes específicas do framework Android (como a Context).

Para ter acesso a API de instrumentação e poder construir testes nesse nível, o Android fornece a classe InstrumentationRegistry. Abaixo podemos ver um exemplo muito simples de como é a estrutura desse tipo de teste:

Exemplo de teste instrumentado com AndroidJUnitRunner. Retirado de: https://www.codevscolor.com/testing-in-android-part-4-instrumented-unit-test/

Percebam que mesmo que estejamos utilizando um emulador ou smartphone para executar esse teste, ele ainda não é considerado um teste de UI. O teste a cima apenas salva um valor em um SharedPreferences e verifica que o valor foi salvo.

Sendo assim, testes que envolvam persistência de dados ou que acessem a algum dado nas pastas de recursos do Android (como strings.xml), podem ser considerados testes instrumentados.

Para ter mais informação sobre esse tipo de teste, acessar o link oficial do developers Android.

Testes com Espresso

Avançando para um nível superior na pirâmide, chegamos na parte de testes de UI com Espresso. O Espresso é uma ferramenta de automação de testes de UI feitas para pessoas desenvolvedoras que possuem mais familiaridade com a codebase. Diferentemente de algumas outras ferramentas de teste de UI, com Espresso você consegue inicializar uma tela diretamente sem passar por todo o fluxo até chegar nela.

A sintaxe de um teste de Espresso é diferente da sintaxe dos testes instrumentados com JUnit. Nele nós temos uma forma de escrever que se assemelha muito com o que um usuário faria na sua App. Basicamente selecionar uma view (onView), fazer alguma ação (perform) e verificar algo (check). Assim como o exemplo abaixo:

Exemplo de teste com Espresso. Retirado de: https://developer.android.com/training/testing/espresso/

O Espresso provê várias formas de buscar views na sua UI e de fazer assertivas sobre elas. Abaixo temos um arquivo que creio ser muito interessante para quem está aprendendo é esse:

Lista de comandos do Espresso. Retirado de: https://developer.android.com/training/testing/espresso/cheat-sheet

Também é interessante mencionar que com o Espresso nós também podemos testar WebViews, acessibilidade e Intents. Pessoalmente eu considero uma ferramenta obrigatória em sua pirâmide de testes Android.

Testes end-to-end com UI Automator (ou Robotium)

O UI Automator é uma ferramenta de testes end-to-end fornecida pela Google. Por ser uma ferramenta oficial, nesse post vamos dar mais foco ao UI Automator do que no Robotium.

A principal diferença entre o Espresso e o UI Automator é que o Espresso testa a UI em um escopo muito mais isolado e o UI Automator consegue testar em um escopo muito mais abrangente que vai além da aplicação que está sendo construída.

Com o UI Automator poderíamos escrever um teste que acesse as configurações do device pelo launcher, ou entre em outro aplicativo. As possibilidade são muito maiores. Além de ter uma grande vantagem que é poder escrever um testes end-to-end num estilo black-box utilizando o mesmo projeto no Android Studio.

Para facilitar a vida de quem escreve esse tipo de teste, foram criadas algumas ferramentas de suporte:

  • UI Automator Viewer: Que é uma ferramenta feita para inspecionar o layout da sua tela com mais detalhes e ajudar a extrair informações como IDs e content descriptions. Essa ferramenta esta localizada na pasta <android-sdk>/tools
  • Classe UiDevice: Classe no código que fornece funções auxiliares como mudar a orientação do device, apertar o botão back ou home e até abrir o menu de notificações.

Abaixo um trecho de código utilizando o UI Automator:

Exemplo de teste com UI Automator. Retirado de: https://developer.android.com/training/testing/ui-automator

Vale mencionar que os testes com UI Automator são os mais pesados e mais sensíveis a mudanças de sistema. Qualquer atualização do Google Play Services ou do próprio sistema operacional podem impactar seu testes. Como a própria documentação oficial mostra:

Cuidado: Recomendamos que você teste sua app usando o UI Automator somente quando precisar interagir com o sistema operacional para atender a um caso de uso crítico. Como o UI Automator interage com outros aplicativos e UI do sistema operacional, pode ser necessário corrigir seus testes após cada atualização do Android. Essas atualizações incluem atualizações de versão da plataforma e novas versões do Google Play Services. (Tradução livre)

Por isso devemos escolher muito bem os testes que queremos testar nessa ferramenta. Se podemos fazer o mesmo teste com Espresso, façamos com Espresso. 😄

Bônus: Testes intermitentes (Flaky tests)

É muito comum termos problemas com testes intermitentes a medida que nossa suíte de testes vai crescendo, especialmente nas partes mais altas da pirâmide. Os motivos podem ser vários:

  • Testes que guardam estado e que são repassados para os próximos testes.
  • Problemas de infraestrutura como baixa memória da máquina que está rodando o teste, especialmente quando se utiliza emuladores.
  • Animações que não são muito bem gerenciadas pelas ferramentas de teste.
  • Problemas de concorrência nas Threads
  • Entre outros….

O grande problema dos testes intermitentes é que eles tiram a confiabilidade da sua suite e atrapalham muito o processo de entrega contínua. Então vou mencionar algumas dicas que podem ajudar a mitigar esse problema no Android:

  • Utilizar o Android Test Orchestrator que cria uma sandbox para cada teste de UI fazendo com que seja muito difícil eles compartilharem estado. A desvantagem é que o Test Orchestrator faz com que sua suite de teste fique bem mais lenta.
  • Remover todas as animações do device que está sendo testado.
  • Fazer uso de IdlingResources, que são operadores que a pessoa que está construindo o teste pode implementar para avisar ao Espresso quando sua UI estará disponível para ser acessada.
  • Nos piores cenários podemos adicionar uma re-execução nos testes que falharam. Assim não necessitamos executar toda a suite de teste novamente, porém essa prática não é recomendada pois não resolve o problema na raiz.

E com isso terminamos a nossa terceira parte da série de posts sobre pirâmide de testes. Na próxima parte vamos explorar um pouco mais sobre os testes end-to-end que são construídos fora da codebase do Android e também vamos explorar um pouquinho dos testes manuais.

Feedbacks também são muito bem-vindos! Obrigado!