Determine o que você precisa testar e o que pode ser descartado.
O artigo anterior abordou os conceitos básicos dos casos de teste e o que eles devem conter. Este artigo aborda a criação de casos de teste de uma perspectiva técnica, detalhando o que deve ser incluído em cada teste e o que evitar. Você vai aprender a resposta para as perguntas antigas "O que testar" ou "O que não testar".
Diretrizes e padrões gerais
É importante observar que padrões e pontos específicos são cruciais, independentemente de você estar realizando testes de unidade, de integração ou completos. Esses princípios podem e devem ser aplicados aos dois tipos de teste, então eles são um bom lugar para começar.
Simplifique
Quando se trata de escrever testes, uma das coisas mais importantes a lembrar é manter a simplicidade. É importante considerar a capacidade do cérebro. O código de produção principal ocupa um espaço significativo, deixando pouco espaço para complexidade adicional. Isso é especialmente verdadeiro para testes.
Se houver menos espaço disponível, você poderá se sentir mais relaxado nos testes. Por isso, é fundamental priorizar a simplicidade nos testes. Na verdade, as práticas recomendadas de teste de JavaScript de Yoni Goldberg enfatizam a importância da Regra de Ouro: o teste precisa ser um assistente, não uma fórmula matemática complexa. Em outras palavras, você precisa entender a intenção do teste à primeira vista.
Você deve buscar a simplicidade em todos os tipos de testes, independentemente da complexidade. Na verdade, quanto mais complexo um teste, mais importante é simplificá-lo. Uma maneira de fazer isso é com um design de teste plano, em que os testes são mantidos o mais simples possível e testam apenas o necessário. Isso significa que cada teste precisa conter apenas um caso de teste, e o caso de teste precisa se concentrar em testar uma única funcionalidade ou recurso específico.
Pense nisso desta perspectiva: deve ser fácil identificar o que deu errado ao ler um teste com falha. Por isso, é importante manter os testes simples e fáceis de entender. Assim, você pode identificar e corrigir problemas rapidamente quando eles surgirem.
Testar o que vale a pena
O design do teste plano também incentiva o foco e ajuda a garantir que os testes sejam significativos. Não crie testes apenas para aumentar a cobertura. Eles precisam ter um propósito.
Não testar detalhes de implementação
Um problema comum nos testes é que eles geralmente são projetados para testar detalhes de implementação, como o uso de seletores em componentes ou testes completos. Os detalhes de implementação se referem a coisas que os usuários do seu código normalmente não usam, não veem ou nem mesmo sabem. Isso pode levar a dois problemas principais nos testes: falsos negativos e falsos positivos.
Falsos negativos ocorrem quando um teste falha, mesmo que o código testado esteja correto. Isso pode acontecer quando os detalhes da implementação mudam devido a uma refatoração do código do aplicativo. Por outro lado, os falsos positivos ocorrem quando um teste é aprovado, mesmo que o código testado esteja incorreto.
Uma solução para esse problema é considerar os diferentes tipos de usuários que você tem. Os usuários finais e os desenvolvedores podem ter abordagens diferentes e interagir com o código de maneiras diferentes. Ao planejar os testes, é essencial considerar o que os usuários vão ver ou com o que vão interagir e fazer com que os testes dependam desses elementos, em vez dos detalhes de implementação.
Por exemplo, escolher seletores menos propensos a mudanças pode tornar os testes mais confiáveis: atributos de dados em vez de seletores CSS. Para mais detalhes, consulte Kent C. Dodds sobre esse assunto ou aguarde um artigo sobre o tema em breve.
Mocking: Don't lose control
A simulação é um conceito amplo usado em testes de unidade e, às vezes, em testes de integração. Isso envolve a criação de dados ou componentes falsos para simular dependências que têm controle total sobre o aplicativo. Isso permite testes isolados.
O uso de modelos nos testes pode melhorar a previsibilidade, a separação de preocupações e o desempenho. E, se você precisar realizar um teste que exija a participação humana (como a verificação de passaporte), será necessário ocultá-lo usando um modelo. Por todos esses motivos, os modelos são uma ferramenta valiosa.
Ao mesmo tempo, a simulação pode afetar a precisão do teste, porque são simulações, não as experiências reais do usuário. Portanto, é preciso ter cuidado ao usar simulações e stubs.
Você precisa criar um modelo para testes completos?
Em geral, não. No entanto, a simulação pode ser útil em alguns casos.
Imagine este cenário: você está escrevendo um teste para um recurso que envolve um serviço de provedor de pagamentos de terceiros. Você está em um ambiente sandbox fornecido por eles, o que significa que nenhuma transação real está ocorrendo. Infelizmente, o sandbox está com defeito, fazendo com que seus testes falhem. A correção precisa ser feita pelo provedor de pagamentos. O que você pode fazer é esperar até que o provedor resolva o problema.
Nesse caso, pode ser mais benéfico diminuir a dependência de serviços que você não pode controlar. Ainda é recomendável usar o mockup com cuidado em testes de integração ou completos, porque ele diminui o nível de confiança dos testes.
Detalhes do teste: o que fazer e o que não fazer
Então, em resumo, o que um teste contém? E há diferenças entre os tipos de teste? Vamos analisar alguns aspectos específicos adaptados aos principais tipos de teste.
O que pertence a um bom teste de unidade?
Um teste de unidade ideal e eficaz precisa:
- Concentre-se em aspectos específicos.
- Operar de forma independente.
- Encompass cenários de pequena escala.
- Use nomes descritivos.
- Siga o padrão AAA, se aplicável.
- Garanta uma cobertura de teste abrangente.
O que fazer ✅ | Não ❌ |
---|---|
Mantenha os testes o mais curtos possível. Teste uma coisa por caso de teste. | Crie testes em unidades grandes. |
Sempre mantenha os testes isolados e simule as coisas que você precisa que estão fora da unidade. | Incluir outros componentes ou serviços. |
Mantenha os testes independentes. | Use testes anteriores ou compartilhe dados de teste. |
Cubra diferentes cenários e caminhos. | Limite-se ao cenário ideal ou testes negativos no máximo. |
Use títulos descritivos para que você saiba imediatamente sobre o que se trata o teste. | Teste apenas pelo nome da função, não sendo descritivo o suficiente: testBuildFoo() ou testGetId() . |
Procure ter uma boa cobertura de código ou uma gama mais ampla de casos de teste, especialmente nesta fase. | Teste de todas as classes até o nível do banco de dados (E/S). |
O que pertence a um bom teste de integração?
Um teste de integração ideal também compartilha alguns critérios com os testes de unidade. No entanto, há alguns outros pontos que você precisa considerar. Um ótimo teste de integração precisa:
- Simule interações entre componentes.
- Cubra cenários reais e use simulações ou stubs.
- Considere a performance.
O que fazer ✅ | Não ❌ |
---|---|
Teste os pontos de integração: verifique se cada unidade funciona bem quando integrada a outra. | Teste cada unidade isoladamente, que é para isso que servem os testes de unidade. |
Testar cenários reais: use dados de teste derivados de dados reais. | Use dados de teste repetitivos gerados automaticamente ou outros dados que não reflitam casos de uso do mundo real. |
Use simulações e stubs para dependências externas para manter o controle do teste completo. | Crie dependências de serviços de terceiros, por exemplo, solicitações de rede para serviços externos. |
Use uma rotina de limpeza antes e depois de cada teste. | Esquecer de usar medidas de limpeza nos testes. Caso contrário, isso pode levar a falhas de teste ou falsos positivos devido à falta de isolamento adequado. |
O que pertence a um bom teste completo?
Um teste completo de ponta a ponta precisa:
- Replique as interações do usuário.
- Encompass cenários vitais.
- Abarca várias camadas.
- Gerenciar operações assíncronas.
- Verifique os resultados.
- Considere a performance.
O que fazer ✅ | Não ❌ |
---|---|
Use atalhos orientados por API. Saiba mais. | Use interações de interface em todas as etapas, incluindo o hook beforeEach . |
Use uma rotina de limpeza antes de cada teste. Tenha ainda mais cuidado com o isolamento de testes do que nos testes de unidade e de integração, porque há um risco maior de efeitos colaterais. | Esquecer de limpar após cada teste. Se você não limpar o estado, os dados ou os efeitos colaterais restantes, eles vão afetar outros testes executados mais tarde. |
Considere os testes completos como testes do sistema. Isso significa que você precisa testar toda a pilha do aplicativo. | Teste cada unidade isoladamente, que é para isso que servem os testes de unidade. |
Use o mínimo ou nenhum mock no teste. Pense bem se você quer simular dependências externas. | Confiar demais em simulações. |
Considere a performance e a carga de trabalho, por exemplo, não testando em excesso cenários grandes no mesmo teste. | Cubra fluxos de trabalho grandes sem usar atalhos. |