Asserção Redundante.
Uma asserção redundante compara um valor consigo mesmo ou com um literal que é igual por construção, de modo que seu resultado é fixo e ela nunca pode realmente falhar ou detectar uma regressão.
##Signs and Symptoms
Uma asserção é redundante quando seu resultado é decidido antes mesmo de o código sob teste rodar — os operandos esperado e real são o mesmo valor, ou ambos são literais sabidamente iguais (ou diferentes). O catálogo xUnit/Test Smells (Peruma et al.) a define como "um método de teste que contém uma instrução de asserção na qual os parâmetros esperado e real são os mesmos," e observa que a asserção, portanto, "é sempre verdadeira ou sempre falsa."
Como identificá-la:
- Um
assertEquals/toBe/toEqualcujos dois argumentos são a mesma expressão ou variável. - Um literal booleano afirmado contra si mesmo, por exemplo
assertTrue(true)ouexpect(true).toBe(true). - Uma comparação que o compilador/linter consegue provar ser constante, como
expect(x === x).toBe(true). - Uma "verificação de sanidade" que reafirma uma constante que você acabou de declarar, em vez de exercitar o sistema.
// Smell: o resultado é fixo, o código sob teste nunca é envolvido
test('user is active', () => {
expect(true).toBe(true); // sempre passa
const status = 'active';
expect(status).toBe('active'); // reafirma o literal, não prova nada
expect(user.id).toEqual(user.id); // valor comparado consigo mesmo
});
Um indício confiável: você pode excluir totalmente o código de produção e a asserção ainda passa verde.
##Reasons for the Problem
Por que acontece
- Resíduo de depuração. O catálogo observa explicitamente que este code smell "é introduzido por desenvolvedores para fins de depuração e depois esquecido" — um espaço reservado
assertTrue(true)embutido no código que sobrevive até o commit. - Cópia e cola / desvio em refatoração. Uma variável é substituída em ambos os lados de um
assertEquals, ou um valor sob teste é renomeado de forma que o esperado e o real colapsam no mesmo símbolo. - Tautologia por construção. Afirmar um valor contra o literal a partir do qual ele acabou de ser atribuído, em vez de contra uma expectativa derivada de forma independente.
- Teatro de cobertura. Uma asserção é adicionada apenas para satisfazer regras de "todo teste deve fazer asserção", sem verificar nada significativo.
Por que prejudica
- Falsa confiança. O teste fica permanentemente verde e conta para o tamanho da suíte e a cobertura, mas não verifica nada. Ele não pode pegar uma regressão, então mascara lacunas na rede de segurança.
- Confiabilidade sem sentido. Um teste que nunca pode falhar fornece sinal zero; um que é sempre falso é peso morto que acaba ignorado ou marcado com
skip. - Legibilidade. Os leitores desperdiçam esforço reconstruindo o comportamento pretendido a partir de uma asserção que afirma uma tautologia; o teste não documenta mais um requisito.
- Manutenibilidade. Asserções redundantes acumulam ruído, inflam métricas e corroem a confiança na suíte, incentivando as pessoas a parar de ler as asserções com atenção.
##Treatment
Substitua a tautologia por uma verificação que vincule um valor esperado conhecido de forma independente ao resultado real produzido pelo sistema sob teste.
Passos:
- Identifique a asserção de resultado fixo — o mesmo operando em ambos os lados, ou dois literais iguais.
- Determine a real intenção. Qual comportamento este teste deveria verificar? Se nenhum, a asserção (ou o teste inteiro) está morta e deve ser removida.
- Faça asserção sobre a saída do SUT, não sobre a entrada. Alimente o sistema com uma entrada real e compare seu resultado calculado a um valor esperado fixo, calculado à mão — não a uma de suas próprias entradas/variáveis.
- Mantenha esperado/real distintos. Garanta que o operando esperado seja uma constante que você escreveu de propósito e que o operando real seja o valor de retorno do código sob teste (isso também corrige o code smell relacionado de ordem errada de argumentos).
- Reexecute com a implementação quebrada (mute-a) para confirmar que a asserção realmente consegue falhar.
// Antes — redundante: o resultado é fixo
test('discount', () => {
const total = 100;
expect(total).toBe(100); // reafirma o literal
expect(applyDiscount).toBe(applyDiscount); // valor vs ele mesmo
});
// Depois — significativo: entrada conhecida -> saída esperada de forma independente
test('applies a 10% discount', () => {
expect(applyDiscount(100, 0.1)).toBe(90); // resultado do SUT vs expectativa calculada à mão
});
Se um espaço reservado como assertTrue(true) ficou da depuração, exclua-o; se ele estava no lugar de uma verificação real, escreva essa verificação.
##Detected by
- sonar javascript:S5863 — Asserções não devem receber duas vezes o mesmo argumento
- sonar java:S5863 — Asserções não devem receber duas vezes o mesmo argumento
- eslint no-constant-binary-expression — Proíbe expressões em que a operação não afeta o valor (sinaliza autocomparações / asserções sempre verdadeiras como assert(a === a))