Os testes automatizados ajudam a melhorar a qualidade do app de várias maneiras. Por exemplo, ela ajuda a realizar a validação, detectar regressões e verificar a compatibilidade. Uma boa estratégia de teste permite aproveitar os testes automatizados para se concentrar em um benefício importante: produtividade do desenvolvedor.
As equipes alcançam níveis mais altos de produtividade quando usam uma abordagem sistemática para testes combinada com melhorias na infraestrutura. Isso fornece feedback oportuno sobre o comportamento do código. Uma boa estratégia de teste faz o seguinte:
- Detecta problemas o mais cedo possível.
- Execução rápida.
- Fornece indicações claras quando algo precisa ser corrigido.
Esta página vai ajudar você a decidir quais tipos de testes implementar, onde e com que frequência executá-los.
A pirâmide de testes
É possível categorizar testes em aplicativos modernos por tamanho. Os testes pequenos se concentram apenas em uma pequena parte do código, o que os torna rápidos e confiáveis. Testes grandes têm um escopo amplo e exigem configurações mais complexas que são difíceis de manter. No entanto, testes grandes têm mais fidelidade* e podem descobrir muito mais problemas de uma só vez.
Fidelidade se refere à semelhança do ambiente de execução de teste com o ambiente de produção.

A maioria dos apps deve ter muitos testes pequenos e relativamente poucos testes grandes. A distribuição de testes em cada categoria precisa formar uma pirâmide, com os testes pequenos mais numerosos formando a base e os testes grandes menos numerosos formando a ponta.
Minimizar o custo de um bug
Uma boa estratégia de teste maximiza a produtividade do desenvolvedor e minimiza o custo de encontrar bugs.
Considere um exemplo de uma estratégia possivelmente ineficiente. Aqui, o número de testes por tamanho não se organiza em uma pirâmide. Há muitos testes de ponta a ponta grandes e poucos testes de componentes da interface:

Isso significa que poucos testes são executados antes da fusão. Se houver um bug, os testes só vão detectá-lo quando os testes de ponta a ponta noturnos ou semanais forem executados.
É importante considerar as implicações disso para o custo de identificar e corrigir bugs e por que é importante direcionar seus esforços de teste para testes menores e mais frequentes:
- Quando um teste de unidade detecta um bug, ele geralmente é corrigido em minutos, o que reduz o custo.
- Um teste completo pode levar dias para descobrir o mesmo bug. Isso pode envolver vários membros da equipe, reduzindo a produtividade geral e potencialmente atrasando um lançamento. O custo desse bug é maior.
Dito isso, uma estratégia de teste ineficiente é melhor do que nenhuma estratégia. Quando um bug chega à produção, a correção leva muito tempo para chegar aos dispositivos do usuário, às vezes semanas. Assim, o ciclo de feedback é o mais longo e caro.
Uma estratégia de teste escalonável
A pirâmide de testes é tradicionalmente dividida em três categorias:
- Testes de unidade
- Testes de integração
- Testes de ponta a ponta.
No entanto, esses conceitos não têm definições precisas. Por isso, as equipes podem definir as categorias de maneira diferente, por exemplo, usando cinco camadas:

- Um teste de unidade é executado na máquina host e verifica uma única unidade funcional de lógica sem dependências do framework Android.
- Exemplo: verificar erros de um em uma função matemática.
- Um teste de componente verifica a funcionalidade ou a aparência de um módulo ou
componente independente de outros componentes no sistema. Ao contrário dos testes de unidade, a área de superfície de um teste de componente se estende a abstrações mais altas acima de métodos e classes individuais.
- Exemplo: Teste de captura de tela para um botão personalizado
- Um teste de recurso verifica a interação de dois ou mais componentes ou módulos independentes. Os testes de recursos são maiores e mais complexos e geralmente operam no nível do recurso.
- Exemplo: testes de comportamento da interface que verificam o gerenciamento de estado em uma tela
- Um teste de aplicativo verifica a funcionalidade de todo o aplicativo
na forma de um binário implantável. São testes de integração grandes que
usam um binário depurável, como um build de desenvolvimento que pode conter hooks de teste,
como o sistema em teste.
- Exemplo: teste de comportamento da UI para verificar mudanças de configuração em um dispositivo dobrável, testes de localização e acessibilidade
- Um teste candidato a lançamento verifica a funcionalidade de um build de lançamento.
Eles são semelhantes aos testes de aplicativos, mas o binário do aplicativo é minificado e otimizado. São testes de integração de ponta a ponta grandes
que são executados em um ambiente o mais próximo possível da produção sem
expor o app a contas de usuário ou back-ends públicos.
- Exemplo: jornadas ideais do usuário, teste de performance
Essa categorização considera fidelidade, tempo, escopo e nível de isolamento. Você pode ter diferentes tipos de testes em várias camadas. Por exemplo, a camada de teste de aplicativo pode conter testes de comportamento, captura de tela e desempenho.
Escopo |
Acesso à rede |
Execução |
Tipo de build |
Ciclo de vida |
|
---|---|---|---|---|---|
Unidade |
Método ou classe única com dependências mínimas. |
Não |
Local |
Depurável |
Pré-fusão |
Componente |
Nível do módulo ou do componente Várias turmas juntas |
Não |
Local |
Depurável |
Pré-fusão |
Recurso |
Nível do recurso Integração com componentes de outras equipes |
Mocked |
Local |
Depurável |
Pré-fusão |
Aplicativo |
Nível do aplicativo Integração com recursos e/ou serviços de outras equipes |
Mocked |
Emulador |
Depurável |
Pré-fusão |
Versão candidata a lançamento |
Nível do aplicativo Integração com recursos e/ou serviços de outras equipes |
Servidor de produção |
Emulador |
Build de lançamento minificado |
Pós-fusão |
Decidir a categoria do teste
Como regra geral, considere a camada mais baixa da pirâmide que pode dar à equipe o nível certo de feedback.
Por exemplo, considere como testar a implementação deste recurso: a interface de um fluxo de login. Dependendo do que você precisa testar, escolha categorias diferentes:
Objeto em teste |
Descrição do que está sendo testado |
Categoria do teste |
Exemplo de tipo de teste |
---|---|---|---|
Lógica do validador de formulários |
Uma classe que valida o endereço de e-mail em relação a uma expressão regular e verifica se o campo de senha foi preenchido. Não tem dependências. |
Testes de unidade |
|
Comportamento da interface do usuário do formulário de login |
Um formulário com um botão que só é ativado quando o formulário é validado |
Testes de componentes |
Teste de comportamento da interface executado no Robolectric |
Aparência da interface do formulário de login |
Um formulário que segue uma especificação de UX |
Testes de componentes |
|
Integração com o gerenciador de autenticação |
A interface que envia credenciais a um gerenciador de autenticação e recebe respostas que podem conter erros diferentes. |
Testes de recursos |
|
Caixa de diálogo de login |
Uma tela mostrando o formulário de login quando o botão de login é pressionado. |
Testes de apps |
Teste de comportamento da interface executado no Robolectric |
Jornada ideal do usuário: fazer login |
Um fluxo de login completo usando uma conta de teste em um servidor de staging |
Versão candidata a lançamento |
Teste de comportamento da interface do Compose completo executado no dispositivo |
Em alguns casos, a classificação de um conteúdo em uma categoria ou outra pode ser subjetiva. Há outros motivos para um teste ser movido para cima ou para baixo, como custo de infraestrutura, instabilidade e tempos de teste longos.
A categoria de teste não determina o tipo de teste, e nem todos os recursos precisam ser testados em todas as categorias.
Os testes manuais também podem fazer parte da sua estratégia de teste. Normalmente, as equipes de controle de qualidade realizam testes de versão candidata, mas também podem estar envolvidas em outras etapas. Por exemplo, testes exploratórios para bugs em um recurso sem um script.
Infraestrutura de teste
Uma estratégia de teste precisa ser apoiada por infraestrutura e ferramentas para ajudar os desenvolvedores a executar testes continuamente e aplicar regras que garantam que todos os testes sejam aprovados.
É possível categorizar os testes por escopo para definir quando e onde executar cada um. Por exemplo, seguindo o modelo de cinco camadas:
Categoria |
Ambiente (onde) |
Gatilho (quando) |
---|---|---|
Unidade |
[Local][4] |
Cada commit |
Componente |
Local |
Cada commit |
Recurso |
Local e emuladores |
Antes da mesclagem ou do envio de uma mudança |
Aplicativo |
Local, emuladores, 1 smartphone, 1 dobrável |
Após a fusão, depois de mesclar ou enviar uma mudança |
Versão candidata a lançamento |
8 smartphones diferentes, 1 dobrável, 1 tablet |
Antes do lançamento |
- Os testes de unidade e componente são executados no sistema de integração contínua para cada novo commit, mas apenas para os módulos afetados.
- Todos os testes de unidade, componente e recurso são executados antes de mesclar ou enviar uma mudança.
- Os testes de aplicativo são executados após a fusão.
- Os testes Release Candidate são executados todas as noites em smartphones, dispositivos dobráveis e tablets.
- Antes de um lançamento, os testes de candidato a lançamento são executados em um grande número de dispositivos.
Essas regras podem mudar com o tempo quando o número de testes afeta a produtividade. Por exemplo, se você mover os testes para uma cadência noturna, poderá diminuir os tempos de build e teste de CI, mas também poderá prolongar o ciclo de feedback.