Explorando a pirâmide de testes no Android — A base da pirâmide — parte 2

Na primeira parte dos posts sobre pirâmide de testes, tivemos uma visão geral dos conceitos e como as ferramentas de testes no Android estão distribuídas dentro da pirâmide. Nesse post vamos explorar a base da pirâmide.

Na base da pirâmide estão os testes mais isolados, rápidos baratos e esses devem ser os de maior quantidade na nossa estratégia de teste. As principais vantagens de ter esse tipo de teste são:

  • Feedback rápido na detecção de erros, já que esses são testes curtos e rápidos.
  • Facilidade de identificar o problema, já que são testes isolados, torna-se mais fácil identificar onde foi o problema.
  • Facilidade na construção, por serem menores e mais específicos.

Dessa forma, eu costumo dizer que na base da pirâmide normalmente se encontram os testes do pacote test e podem ser categorizados como testes unitários puros e testes com a ferramenta Robolectric.

Exemplo de pirâmide de testes no Android com foco na base

Testes unitários puros

Antes de saber o que são esses testes, precisamos entender o conceito de separação de código do framework Android e do código estritamente da linguagem.

Devemos lembrar que os testes do pacote test não fazem uso de emulador ou smartphone e as classes do framework Android não vão ser acessíveis por padrão. Essas classes do Android (que não seriam acessíveis) são aquelas que estão incluídas no SDK da plataforma e possuem dependência direta ou indireta com a classe Context, uma das principais classes do ambiente Android.

Sendo assim, os testes unitários puros são aqueles que não contém qualquer classe do framework Android, podendo ser executados apenas com JUnit e Mockito.

Para que esses testes unitários sejam construídos de maneira efetiva é muito importante que sua aplicação tenha uma arquitetura bem estruturada. Dentre as arquiteturas conhecidas nós temos MVP, MVVM, VIPER, MVI e etc…

Abaixo temos um exemplo de como uma arquitetura bem definida poderia nos ajudar:

Exemplo de Arquitetura VIPER customizada no Android

Nesse exemplo de Arquitetura VIPER simplificada separamos as camadas em duas cores. Na cor azul estão as camadas que podem ser testadas unitariamente com JUnit e Mockito, pois pertencem apenas ao Java ou Kotlin. Na cor verde estão as camadas que necessitam do framework Android para serem testadas corretamente (logo precisam de um emulador ou um device rodando). Abaixo um pouco mais de detalhes sobre as camadas em verde:

  • Camada View contém classes do Android como Activity, Fragment e outros componentes visuais.
  • Camada Router-Navigator contém classes como Intent que precisam de Context para realizar a navegação.
  • Camada Local Repository pode conter classes como SharedPreferences e outras bibliotecas de armazenamento de dados que podem precisar de Context.

Para conseguir construir os testes unitários na base da pirâmide, recomenda-se a utilização dos princípios de injeção de dependências para que seus testes fiquem bem mais simples e isolados. Seguindo esse princípio e possuindo uma arquitetura bem definida, com certeza você estará no caminho certo. 💪

Testes com Robolectric

Para quem não conhece, o Robolectric é um framework de testes que consegue emular todos os recursos do Android sem a necessidade de um emulador ou smartphone. Por exemplo, com ele é possível fazer testes que dependem de Context ou de outras classes do Android de maneira mais rápida e fácil dentro do pacote test (e em breve no pacote androidTest também).

O Robolectric realmente faz mágica e já é considerada uma ferramenta bem madura dentro da comunidade Android. Seus benefícios realmente são inúmeros, porém gostaria de deixar duas observações que considero muito importantes.

Primeira observação: Os testes de Robolectric não são unitários!

Apesar de na página principal do Robolectric ele se definir como "unit test framework", ele pode ou não testar unitariamente. Veja um exemplo de um Hello World com Robolectric abaixo:

Exemplo de teste com Robolectric. https://gist.github.com/Shebella/aae94c2fdf758296942f

No teste temos uma Activity que contém um Button e um TextView. Ao clicar no botão um resultado vai aparecer no campo de texto. Baseado nesse teste, podemos refletir sobre dois pontos:

  • O teste é muito parecido com um teste de UI… 🤔
  • Quantas camadas ou classes eu tive que acessar para buscar esse resultado? Não é muito fácil perceber isso nesse teste…

Podemos facilmente pensar em um cenário em que o resultado do TextView estava armazenado em uma base de dados local. Dessa forma, podemos pensar que um teste que vai da Activity até a base de dados não poderia ser considerado unitário. Como também consigo pensar em um cenário em que o resultado do TextView estava dentro da própria Activity e poderia ser considerado unitário.

Apresentação do Robolectric como parte do framework oficial de testes do Android no Google IO 2018

No Google IO 2018 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.

Como opinião pessoal, creio que isso é uma evidência de que o próprio Google também não considera os testes de Robolectric como unitários e que também veem muito potencial nessa ferramenta de testes.

Segunda observação: Os testes de Robolectric não garantem que seu código está bem modularizado ou desacoplado!

Existe o senso comum de que os testes unitários só podem ser feitos quando temos um código modularizado, desacoplado e com uma boa arquitetura. Nesse momento chega o Robolectric que nos ajuda a testar emulando os recursos do Android com um estilo de teste muito parecido com o unitário.

Começamos a construir vários testes sem pensar como está nosso código de produção e aí surge a falsa sensação dentro das equipes de desenvolvimento:

"Se eu consigo fazer testes sem emulador e no pacote test, logo meu código está desacoplado e nossa arquitetura está funcionando bem, Uhuuuu!"

A afirmação a cima nem sempre é correta, pois pela facilidade que o Robolectric nos dá para construir testes, conseguimos fazê-los mesmo que nosso código esteja acoplado e mal arquitetado.

Em equipes inexperientes, isso pode causar a falsa sensação de que o código que vai para produção está muito bom mesmo que não esteja. Por isso concluo que o Robolectric é uma ferramenta excelente e ela deve fazer parte da sua pirâmide, porém utilize com cuidado e responsabilidade!

Com isso terminamos a segunda parte da série de posts sobre pirâmide de testes. Na próxima parte vamos explorar um pouco mais dos testes instrumentados dentro do Android Studio.

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