Testar ou não: do ponto de vista técnico

Determine o que você precisa testar e o que pode descartar.

O artigo anterior abordou os conceitos básicos dos casos de teste e o que eles precisam conter. Este artigo detalha 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. Basicamente, você vai aprender a resposta para as antigas perguntas "O que testar" ou "O que não testar".

O que testar ou não.

Diretrizes e padrões gerais

É importante notar que pontos e padrões específicos são cruciais, não importa se você está realizando testes de unidade, integração ou completos. Esses princípios podem e devem ser aplicados a ambos os tipos de teste, portanto são um bom ponto de partida.

Simplifique

Quando se trata de criar testes, um dos pontos mais importantes a ser lembrado é simplificar. É 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 se aplica especialmente aos testes.

Se houver menos espaço livre disponível, você pode relaxar mais nos esforços de teste. É por isso que é crucial priorizar a simplicidade nos testes. Na verdade, as práticas recomendadas de teste em JavaScript de Yoni Goldberg enfatizam a importância da Regra de Ouro: seu teste deve ser como um assistente e não como uma fórmula matemática complexa. Em outras palavras, é preciso entender rapidamente a intent do teste.

Não torne os testes complexos, porque eles não devem se sentir assim.

É importante manter a simplicidade em todos os tipos de testes, independente da complexidade. Na verdade, quanto mais complexo for um teste, mais importante será simplificá-lo. Uma maneira de conseguir isso é por meio de um design de teste plano, em que os testes são o mais simples possível e testam apenas o que é necessário. Isso significa que cada teste deve conter apenas um caso de teste, e o caso de teste deve se concentrar em testar uma única funcionalidade ou recurso específico.

Pense sobre isso desta perspectiva: a identificação do que deu errado ao ler um teste reprovado precisa ser fácil. É por isso que é importante manter os testes simples e fáceis de entender. Assim, é possível identificar e corrigir rapidamente os problemas assim que eles surgirem.

Teste o que vale a pena

O design do teste simples também incentiva a concentração e ajuda a garantir que os testes sejam significativos. Lembre-se de que os testes não devem ser criados apenas para fins de cobertura. Eles precisam ter um propósito.

Não teste todas as coisas.

Não testar detalhes da implementação

Um problema comum durante testes é que os testes geralmente são projetados para testar detalhes de implementação, como o uso de seletores em componentes ou testes de ponta a ponta. Os detalhes de implementação se referem a coisas que os usuários do seu código normalmente não usarão, verão ou não conhecerão. Isso pode causar dois grandes problemas nos testes: falsos negativos e falsos positivos.

Os 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 que está sendo testado esteja incorreto.

Uma solução para esse problema é considerar os diferentes tipos de usuários que você tem. Os desenvolvedores e usuários finais podem ter abordagens diferentes e interagir com o código de maneiras diferentes. Ao planejar testes, é essencial considerar o que os usuários vão acessar ou com o que vai interagir, além de tornar os testes dependentes desses elementos, e não dos detalhes da 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 de CSS. Para mais detalhes, consulte Kent C. Dodds sobre esse assunto, ou fique de olho, um artigo sobre esse assunto será lançado em breve.

Simulação: não perca o controle

A simulação é um conceito amplo usado em testes de unidade e, às vezes, em testes de integração. Ela envolve a criação de dados ou componentes falsos para simular dependências com controle total sobre o aplicativo. Isso permite testes isolados.

O uso de simulações nos testes pode melhorar a previsibilidade, a separação de conceitos e o desempenho. Se você precisar realizar um teste que exija envolvimento humano (como a verificação de um passaporte), terá que ocultá-lo usando uma simulação. Por todos esses motivos, os mockups são uma ferramenta valiosa a se considerar.

Ao mesmo tempo, a simulação pode afetar a precisão do teste porque eles são simulações, não as experiências reais do usuário. Portanto, você precisa ter cuidado ao usar modelos e stubs.

Você deve simular nos testes de ponta a ponta?

Em geral, não. No entanto, às vezes, zombar pode ser muito útil. Por isso, não vamos descartar isso totalmente.

Imagine este cenário: você está criando um teste para um recurso que envolve um serviço de provedor de pagamento terceirizado. Você está em um ambiente de sandbox fornecido por ele, o que significa que nenhuma transação real está ocorrendo. Infelizmente, o sandbox está com problemas, fazendo com que seus testes falhem. A correção precisa ser feita pelo provedor de pagamento. Basta esperar que o problema seja resolvido pelo provedor.

Nesse caso, pode ser mais vantajoso diminuir a dependência de serviços que você não pode controlar. Ainda é aconselhável usar a simulação com cuidado em testes de integração ou de ponta a ponta, porque isso diminui o nível de confiança dos seus testes.

Detalhes do teste: o que fazer e o que não fazer

De modo geral, 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 faz um bom teste de unidade?

Um teste de unidade ideal e eficaz precisa:

  • Concentre-se em aspectos específicos.
  • Opere de forma independente.
  • Abrange cenários de pequena escala.
  • Use nomes descritivos.
  • Siga o padrão AAA, se aplicável.
  • Garantir cobertura abrangente de teste.
O que fazer ✅ O que não fazer ❌
Mantenha os testes os menores possíveis. Teste uma coisa por caso de teste. Programe testes em unidades grandes.
Sempre mantenha os testes isolados e simule o que você precisa que esteja fora da sua unidade. Inclua outros componentes ou serviços.
Manter os testes independentes. Confie em testes anteriores ou compartilhe dados de teste.
Aborde diferentes cenários e caminhos. Limite-se ao caminho da felicidade ou a testes negativos no máximo.
Use títulos de teste descritivos, para que você saiba imediatamente do que se trata o teste. Teste apenas pelo nome da função, que não é descritivo o suficiente como resultado: testBuildFoo() ou testGetId().
Tente ter uma boa cobertura de código ou uma variedade mais ampla de casos de teste, especialmente nessa fase. Faça testes com todas as classes até o nível do banco de dados (E/S).

O que faz parte de 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á mais alguns pontos que você precisa considerar. Um ótimo teste de integração precisa:

  • Simule interações entre componentes.
  • Aborde cenários reais e use rascunhos ou rascunhos.
  • Considere o desempenho.
O que fazer ✅ O que não fazer ❌
Teste os pontos de integração: verifique se cada unidade funciona corretamente em conjunto quando integrada uma com a outra. Teste cada unidade de forma isolada. É para isso que servem os testes de unidade.
Testar cenários reais: usar dados de teste derivados de dados do mundo real. Use dados de teste repetitivos gerados automaticamente ou outros dados que não refletem casos de uso do mundo real.
Use simulações e stubs para dependências externas com o objetivo de manter o controle do teste completo. criar dependências em serviços de terceiros, como solicitações de rede para serviços externos;
Use uma rotina de limpeza antes e depois de cada teste. Esqueça de usar medidas de limpeza nos testes. Caso contrário, isso pode causar falhas nos testes ou falsos positivos devido à falta de isolamento adequado.

O que faz parte de um bom teste de ponta a ponta?

Um teste completo e abrangente precisa:

  • Replique as interações do usuário.
  • Inclua cenários vitais.
  • abrange várias camadas.
  • Gerencie operações assíncronas.
  • Verifique os resultados.
  • Considere a performance.
O que fazer ✅ O que não fazer ❌
Usar atalhos orientados por API. Saiba mais. Use interações de interface para cada etapa, incluindo o hook beforeEach.
Use uma rotina de limpeza antes de cada teste. Cuide ainda mais do isolamento de testes do que dos testes de unidade e integração, porque há um risco maior de efeitos colaterais. Esquecer de fazer a limpeza após cada teste. Se você não limpar o estado restante, os dados ou os efeitos colaterais, eles afetarão outros testes executados mais tarde.
Considerar testes completos como testes do sistema. Isso significa que você precisa testar toda a pilha de aplicativos. Teste cada unidade de forma isolada. É para isso que servem os testes de unidade.
Use pouca ou nenhuma simulação dentro do teste. Pense bem se quiser simular dependências externas. Confie muito em simulações.
Considere o desempenho e a carga de trabalho, por exemplo, não testando grandes cenários no mesmo teste. Cubra grandes fluxos de trabalho sem usar atalhos.