Higiene da suíte de testes: Quando um teste deixa de proteger o que importa

Há algumas semanas apresentei para um time de QA um tema que me acompanha há tempos, mas que eu nunca tinha nomeado direito: higiene da suíte de testes. Foi uma apresentação curta, construída a partir de uma situação comum em projetos de software. Funcionou bem no formato de reunião, mas saí de lá com a sensação de que o assunto merecia mais espaço do que alguns slides permitem. Este artigo é essa continuação.

O Paradoxo do Pesticida: testes que só sabem lutar contra o passado

Eu não li o livro Software Testing Techniques, de Boris Beizer. Conheci o Paradoxo do Pesticida há bastante tempo, quando estudei o syllabus para conquistar a certificação CTFL. Mais recentemente, o artigo The Pesticide Paradox me ajudou a refrescar esse princípio e a retomar uma reflexão que fazia muito sentido para o tema desta publicação.

A ideia é simples: se repetimos os mesmos testes indefinidamente, eles tendem a parar de encontrar bugs novos. É uma analogia com a agricultura: pragas desenvolvem resistência ao mesmo inseticida usado repetidamente, e o produto que antes funcionava vai perdendo efeito.

Com testes automatizados acontece algo parecido. O sistema evolui, novas regras de negócio nascem, integrações mudam, mas a suíte continua testando exatamente as mesmas coisas. O teste não está necessariamente errado; ele pode estar protegendo um sistema que já não existe daquela forma.

Um pipeline verde não significa que está tudo certo. Significa que os testes escritos não encontraram problema naquilo que sabem procurar.

O que é, na prática, higiene da suíte de testes

Não confunda com sanitização de dados de entrada. Aqui, o assunto é a saúde da própria suíte de automação, não os dados que ela consome.

Higienizar uma suíte é revisar criticamente cada teste e perguntar se ele ainda merece o lugar que ocupa. Na minha experiência, os candidatos a poda geralmente caem em três categorias:

Tipo de teste problemático Sintoma O que fazer
Obsoleto Valida uma funcionalidade que já não existe ou mudou de comportamento. Remover: manter é custo sem benefício.
Redundante Cobre exatamente o mesmo comportamento que outro teste já garante. Consolidar, idealmente com parametrização.
De baixo valor É genérico demais; passa ou falha sem dizer nada sobre a regra de negócio. Substituir por testes que validem o comportamento relevante.

O ganho não é só estético. Uma suíte mais enxuta roda mais rápido, custa menos para manter e devolve confiança: quando um teste falha, você sabe que algo relevante quebrou, não que mais um teste frágil resolveu reclamar.

Um exemplo genérico: de “o pedido foi criado?” para “o frete foi calculado certo?”

Para explicar a ideia sem expor processos, dados ou regras de uma empresa, imagine um fluxo comum de checkout em um e-commerce. Existe um teste antigo que confirma apenas se um pedido foi criado depois da finalização da compra:

it("Cria o pedido ao finalizar a compra", () => {
  finalizarCompra();

  cy.request("GET", `/api/orders/${orderId}`).then((response) => {
    expect(response.status).to.eq(200);
    expect(response.body.id).to.exist;
  });
});

É um teste de sanidade honesto, mas raso: ele confirma que algum pedido existe. Não verifica se a regra de frete foi aplicada corretamente, que é justamente o tipo de comportamento que pode gerar prejuízo ou reclamação.

Uma revisão com produto, desenvolvimento e suporte pode revelar dúvidas recorrentes: frete grátis acima de determinado valor, cobrança normal abaixo do limite, endereço não atendido e pedido com cupom. Em vez de manter um teste genérico isolado, podemos substituí-lo por cenários que expressem essas regras:

[
  { descricao: "frete grátis acima do limite", subtotal: 250, freteEsperado: 0 },
  { descricao: "cobra frete abaixo do limite", subtotal: 120, freteEsperado: 18.90 },
  { descricao: "endereço não atendido", subtotal: 250, freteEsperado: null },
  { descricao: "cupom não altera a regra de frete", subtotal: 250, freteEsperado: 0 }
].forEach(({ descricao, subtotal, freteEsperado }) => {
  it(descricao, () => {
    prepararCarrinho({ subtotal });
    finalizarCompra();
    validarFrete(freteEsperado);
  });
});

Os cenários continuam garantindo que o pedido foi criado, mas essa verificação agora faz parte de uma validação mais valiosa. A suíte não perdeu cobertura ao remover o teste genérico: ela absorveu essa cobertura enquanto passou a proteger regras que realmente importam.

Se tudo que um teste valida já acontece como pré-condição de outro teste mais robusto, ele provavelmente virou peso morto.

Um efeito colateral bom: testes que se autopreparam

Ao reescrever cenários assim, aparece outro ganho importante: o teste deixa de depender de um carrinho ou pedido deixado em um estado específico por uma execução anterior.

const prepararCarrinho = ({ subtotal }) => {
  return consultarCarrinho().then((carrinho) => {
    if (carrinho.itens.length > 0) {
      return limparCarrinho().then(() => adicionarItens(subtotal));
    }

    return adicionarItens(subtotal);
  });
};

Na prática, a massa de teste pode permanecer estática. O cenário verifica o estado atual e corrige o que for necessário antes de começar. Se a limpeza falhar, o teste falha naquele passo, com uma causa clara, em vez de apresentar uma falha enganosa na regra de frete.

Essa ideia conversa com o princípio de isolamento descrito por Martin Fowler em Eradicating Non-Determinism in Tests: um teste confiável reconstrói seu estado inicial ou garante sua própria limpeza. O que ele não pode fazer é assumir que o ambiente está do jeito que espera.

Um framework simples para praticar no dia a dia

  1. Revise e questione. O teste ainda é relevante? A funcionalidade continua existindo? Outro teste já cobre o mesmo comportamento como pré-condição?
  2. Refatore e consolide. Parametrize cenários semelhantes e centralize a lógica repetida em funções reutilizáveis.
  3. Torne os testes independentes. Prepare e valide o estado dos dados antes do cenário, separando falha de preparação de falha de regra de negócio.

Testes executados com frequência ainda podem sofrer com dados que se corrompem entre execuções. Independência não é luxo: é o que permite rodar a suíte sem intervenção manual e confiar no significado de cada falha.

Conclusão: higiene é cultura, não checklist

Nenhum sistema grande terá 100% de cobertura automatizada, e tudo bem. O objetivo nunca foi automatizar tudo, mas proteger com automação aquilo que realmente importa para o negócio.

Higiene de suíte não é uma tarefa de faxina feita uma vez e esquecida. É uma conversa contínua com as pessoas que conhecem o produto, uma revisão frequente das regras e uma disposição para aposentar testes que já não entregam valor. A qualidade da suíte precisa evoluir junto com a qualidade do sistema que ela protege.

Leitura Recomendada

💼 Leve essa discussão adiante!

Se este artigo sobre higiene de testes fez sentido para você, que tal compartilhar a reflexão?

"Testes automatizados não envelhecem bem sozinhos. Uma reflexão sobre o Paradoxo do Pesticida e como um caso genérico pode dar lugar a testes de regra de negócio, manutenção e independência."

← Voltar para publicações