Testes Instrumentados: tipos de teste e como são executados.

Natan Ximenes
Android Dev BR
Published in
8 min readMar 18, 2021

--

Sim, 2021 e eu ainda estou aqui falando sobre testes…

Trabalho com produtos digitais há alguns anos, e é muito comum se ver num momento/ambiente onde fazer testes é algo considerado como uma etapa, e não como parte do desenvolvimento. É mais comum do que você imagina..

Aquela versão que precisa ser lançada pra ontem, acaba indo para produção, sem testes, porque P.O/ P.M /S.M /Head /Stakeholder /Skateholder /etc testou manualmente e “como a tarefa está pronta, só faltam testes, da pra priorizar os testes depois.

É uma situação bem chata, né? A qualidade é essencial, durante o desenvolvimento de uma aplicação, para que os nossos usuários tenham em mãos um aplicativo que atenda suas necessidades, sem que nenhum comportamento inesperado afete sua experiência. Porém, ainda assim, a situação relatada acontece, esporádicamente, nas melhores famílias.. 🤷‍♂️

Precisamos ter a capacidade, técnica e argumentativa, para demonstrar porque os testes são super importantes e não devem ser deixados de lado.

Dito isso, tem que ser de comum entendimento que os testes ajudam a garantir a qualidade de uma aplicação e são a base de validação do que construímos no dia a dia.

Porém, quando se fala sobre testes, existem uma infinidade de tipos de teste que nos vem à cabeça(ou quando pesquisamos no Google).

Tipos de Teste

tipos de teste

Dentre todos esses tipos de teste, destaco 4 deles(Integração, Unitários, Fucionais e Manuais) juntamente com os Instrumentados.

Esses 4 tipos testes destacados são essenciais no contexto de mobile, pois:

  • Os Testes Unitários vão garantir que as menores unidades(conjunto de 1..n classes) do app funcionam como deveriam, de forma isolada, a partir de validações de entrada e saídas esperadas.
  • Os Testes de Integração, vão garantir que as unidades do nosso app funcionam de forma integrada, através da interação entre as unidades.
  • Os Testes Funcionais vão garantir que nosso app funciona como esperado, no ponto de vista das regras de negócio, validando, de ponta a ponta, os fluxos de tela que um usuário faria.
  • O Teste Manual se encaixa no Teste Funcional, como um teste de UI também. Porém, é um teste não automatizado, que está sujeito ao erro humano, visto que uma falta de atenção pode considerar o resultado positivo de um teste como falha ao invés de sucesso, ou visse e versa.

Dito isso, podemos finalmente falar sobre o Teste Instrumentado e como ele pode estar relacionado a mais de um desses tipos de teste.

Instrumentação

Primeiramente, precisamos falar sobre Instrumentação, que no contexto de desenvolvimento de software, diz respeito a ferramentas que forneçam formas de gerenciar, medir ou controlar um sistema. Exemplos: Analytics, Crashlytics, Logs, Android Instrumentation, etc.

No caso dos Testes Instrumentados, o framework de testes do Android nos permite fazer isso, controlando o ciclo de vida da aplicação sob teste, realizando ações de interação visuais e asserções sobre o estado atual da estrutura hierárquica de views da tela.

Testes Instrumentados

Um Teste Instrumentado, é um teste que é executado num device/emulador, e através da instrumentação, provida pelo Android Instrumentation, é possível ter acesso a informações sobre a aplicação, dando a possibilidade de controlá-la, da forma desejada, se necessário.

Isso nos dá a possibilidade de ter dois tipo de testes instrumentados:

Teste Instrumentado UI

A forma mais difundida, que praticamente todos nós, Devs Androids, conhecemos(? 👀). É um teste de UI, focado em realizar asserções que validem os estados desejados da tela, de acordo com os inputs recebidos, normalmente na forma de interações, que simulam o que um usuário faria. Validações e Inputs esses realizados através da api de teste, o Espresso. Abaixo, temos um exemplo simples de teste instrumentado usando-o.

Sua execução é realizada num device real ou num emulador.

Ao iniciar a task de teste instrumentado(via Android Studio ou na linha de comando, via Gradle), logo após o build, são gerados dois apks, que são instalados em conjunto. Um Apk contendo o app de fato, e um outro apk contendo todas as instruções de ação e asserção do teste.

A instrumentação é inicializada numa thread separada, cuja qual é responsável por realizar todo o controle e instrumentação necessária para as validações e, logo depois, o framework inicializa o nosso app, diretamente na activity sob teste. Tudo isso é possível porque os dois apks rodam no mesmo processo, permitindo essa comunicação entre o apk do app e o apk de testes.

Assim, de forma resumida, a estrutura de um app sob teste pode ser exemplificada através da figura abaixo:

Do ponto de vista do objetivo do Teste Instrumentado de UI, é bom pontuar que o Espresso nos provê ferramentas na API que são modeladas para validação de uma tela(Activity, Fragment, View), que pode ser chamada isoladamente, sem a necessidade de percorrer um fluxo. Caracterizando assim um teste focado na validação de uma tela de forma absoluta.

Não há necessidade de realizar navegação de telas em tempo de execução de teste instrumentado, pois isso deixaria o teste mais complexo e mais lento, visto que mais preparações seriam necessárias, para cada tela que fosse executada. Preparações como: Injeção de dependências, mocks, fake objects e por aí vai…

O Espresso provê a praticidade de abrir qualquer tela(activity/fragment) de um fluxo, de forma isolada, sem necessidade de navegação.

Justamente nesse cenário, podemos fazer um paralelo com Testes Unitários, onde a “unidade” seria a tela. Isso quer dizer que o foco da validação estaria apenas na camada de apresentação. Assim, as outras camadas(domínio e dados) poderiam ser mockadas, para evitar que sejam executadas, em tempo de teste. Isso traz um excelente ganho, no tempo de execução do teste, pois menos classes seriam executadas, assim como num Testeu Unitário. Tente isolar ao máximo Activities e Fragments, ao testá-los, injetando mocks/fakes das suas dependências.

É possível também criar um paralelo com Testes de Integração, se a estratégia de testes estiver focada em validar as interações entre as classes que compõem a camada de Apresentação, Domínio e Dados. Assim, durante a execução do testes instrumentado de UI, tudo seria executado, inclusive regras de negócio, transformações de dados e requisições a APIs externas(mockadas e interceptadas pelo MockWebServer).

Caso seja necessário realizar validações de fluxos de tela, end-to-end, há alternativas de ferramentas de testes funcionais como o Appium, que é feito para isso. Também é possível utilizar o Espresso para validações de fluxo de tela, visto que sua execução é mais rápida que o Appium. Considere o que for melhor, de acordo com a sua estratégia de testes.

Teste unitários instrumentados

Algumas vezes se faz necessário validar unitariamente uma classe comum(sem ser Fragment/Activity/Custom View), que dependa do framework Android.

Nesse cenário, ao invés de o teste unitário ser executado na JVM, ele é executado na ART(Android Runtime), a partir de um device, garantindo que os testes tenham as dependências reais de tudo que for relacionado ao Android. Esses testes podem utilizar simplesmente o Espresso, Mockito e o JUnit, dependendo da complexidade do que se precisa testar.

Para facilitar o entendimento, vamos imaginar que temos uma classe chamada FileUtils, responsável por lidar com operações relacionadas a arquivos. Dentro dela, temos a função getInternalDirectoryFilesListSize, que é responsável por dizer a quantidade de arquivos dentro de um diretório, localizado no armazenamento interno do app.

Perceba que a classe acima não tem nada relacionado a Activity, Fragment ou View. É apenas uma simples classe, que utiliza Context e File, duas classes do framework Android. O Context é necessario para setar o diretorio do armazenamento interno do app, onde sera realizada a busca pela quantidade de arquivos.

Como classes do framework Android não são carregadas automaticamente na JVM, quando executamos testes unitarios, a implementação de Testes unitarios para essa classe ficar inviável, visto que não seria possivel ter acesso a uma instancia de Context que está sempre associada a Applications, Activities ou Fragments. (Pensou no Roboletric? ja ja falo sobre ele!)

Esse é exatamente um caso onde podemos criar um Teste Unitário Instrumentado, que irá garantir que nossa função consegue nos trazer a informação correta de um diretório criado no armazenamento interno do nosso app.

Testes Unitarios Instrumentados devem estar dentro do source set src/androidTest, e não em src/test, que é o source set dos testes unitarios. Caso contrario, não serão executados num device.

O teste ficaria localizado no path: src/androidTest/FileUtilsTest.kt

O teste acima, cria 3 arquivos num dado diretório e chama a classe FileUtils, passando o mesmo diretório, validando se a mesma retorna a informação correta sobre o número de arquivos criados.

Assim como nos Testes Instrumentados de uma UI, sua execução é realizada num device real ou num emulador e, nesse momento, também são instalados dois apks(apk de teste e apk do app). A instrumentação é inicializada, da mesma maneira, numa thread separada, porém, como não é um teste que valida uma UI, não há necessidade de abrir nenhuma tela aqui.

Roboletric

Existe uma lib chamada Roboletric, que torna viável executar testes de classes que tenham dependências do framework Android, a partir da própria JVM(sua máquina), sem necessidade de executar em devices/emuladores. Assim é possível também criar Headless UI tests(testes de UI que rodam sem necessidade de renderização) com o Roboletric.

A lib cuida de criar uma série de fakes/mocks dos componentes do Android, e os carrega em memória, na JVM, para que seja possível acessá-los em tempo de teste. Esses mocks/fakes são chamados de Shadows. Você pode ler a documentação da lib aqui.

Entretanto, esse processo de carregamento, traz um overhead para a execução, deixando um pouco mais lento, que um Teste unitário comum de uma classe que tenha apenas dependências de Kotlin/Java.

Por outro lado, um Teste Instrumentado de UI, executado de forma headless, com a ajuda do Roboletric, é mais rápido do que se comparado a sua execução num device real.

É necessário tomar um cuidado com essa abordagem, pois, como o Roboletric cria uma série de objetos fakes do framework do Android, há uma perda de fidelidade do teste, pois estaria sendo executado num ambiente fake e não num ambiente real(device).

Assim, não há um certo ou errado. Depende apenas das necessidades do seu time e das suas escolhas para a sua Estratégia de Testes.

Espero que este artigo possa ter ajudado você a entender mais sobre os Testes Instrumentados. Se você tem alguma opinião sobre, por favor, deixe um comentário abaixo. Além disso, caso tenha alguma duvida, sinta-se à vontade pra me procurar no Twitter: @natanximenesdev

Valeu!

Curtiu o post? então divulga pras pessoas que querem entender um pouco mais sobre testes no Android. Meu objetivo é passar conhecimento adiante e trocar experiências!

Referências

Criei uma lista de refência, contendo uma série de links de artigos, videos e etc, sobre Testes de forma geral e Testes no Android, da uma conferida aqui:

--

--

Natan Ximenes
Android Dev BR

Senior Software Engineer, Android @ League, passionate about Android development, Agile and Digital products.