ConstructiCat Logo
CodeBust.
Browse section ▾

Duplicate Assert.

Um único método de teste verifica a mesma condição mais de uma vez — repetindo uma asserção idêntica ou reverificando uma lógica equivalente — em vez de remover a verificação redundante ou separar casos distintos em seus próprios testes focados.

##Signs and Symptoms

Você vê a mesma asserção aparecer duas vezes (mesmo matcher, mesmos argumentos) em um único teste, ou vários blocos de asserção copiados e colados que diferem apenas nos valores literais e re‑testam o mesmo comportamento. Sinais reveladores:

  • Uma linha de asserção literalmente idêntica aparece duas ou mais vezes no corpo.
  • Muitas chamadas expect(...)/assertEquals(...) exercitando a mesma condição com entradas diferentes, todas amontoadas em um único método cujo nome descreve apenas um único cenário.
  • Asserções remanescentes de depuração ("deixa eu também verificar X") que nunca foram removidas.
  • O nome do teste (por exemplo, testXmlSanitizer) não dá nenhuma pista sobre qual de suas muitas verificações falhou.
test('sanitizer accepts valid input', () => {
  expect(isValid('plain text')).toBe(true);
  expect(isValid('with spaces')).toBe(true);
  expect(isValid('Fritz-box')).toBe(true);   // "menos é válido"
  expect(isValid('Fritz-box')).toBe(true);   // <-- duplicata exata, não acrescenta nada
  expect(isValid('<script>')).toBe(false);
});

A linha Fritz-box duplicada é o Duplicate Assert canônico; o smell mais amplo é que um único método agrupa silenciosamente muitas verificações da mesma condição sob um único nome.

##Reasons for the Problem

Por que acontece

  • Copiar e colar. Um bloco de asserção é duplicado e os literais (ou nem isso) são alterados.
  • Resíduo de depuração. Asserções extras adicionadas para investigar o comportamento são deixadas para trás.
  • Agrupamento. Os desenvolvedores testam "um método" empilhando todos os casos em um único teste em vez de separá-los, produzindo verificações repetidas e quase idênticas.

Por que é prejudicial

  • Falsa confiança. Uma asserção duplicada e verdadeiramente idêntica acrescenta zero cobertura — ela nunca pode falhar quando sua gêmea passa — e ainda assim faz o teste parecer mais completo do que é.
  • Diagnóstico de falha difícil. Quando várias asserções com a mesma forma compartilham um único método, o relatório de falha aponta para o método, não para a entrada específica que quebrou. Pior ainda, um teste com configuração padrão para na primeira asserção que falha, de modo que as verificações duplicadas posteriores nunca são executadas — você corrige uma, executa de novo, esbarra na próxima (sobrepõe-se ao Assertion Roulette).
  • Legibilidade ruim. O leitor não consegue dizer se a repetição é intencional (casos distintos) ou um erro (duplicata real), e o nome do teste documenta apenas uma de muitas condições.
  • Custo de manutenção. Mude o comportamento sob teste e você terá que caçar e atualizar cada asserção duplicada; se errar uma, a suíte fica inconsistente.

##Treatment

  1. Exclua duplicatas exatas. Se uma asserção é byte a byte idêntica a outra no mesmo teste, remova-a — é peso morto, não cobertura.
  2. Parametrize casos equivalentes. Quando as "duplicatas" são na verdade a mesma condição com entradas diferentes, converta-as em um teste de tabela/parametrizado (test.each no Jest/Vitest, @ParameterizedTest no JUnit 5). Cada linha é reportada e nomeada separadamente, de modo que as falhas apontam exatamente para a entrada problemática.
  3. Separe condições genuinamente diferentes em testes distintos, cada um com um nome que declare o que verifica (aceita nomes de host com hífen, rejeita tags de script).
  4. Nomeie pela intenção. O nome de um teste deve descrever um único comportamento; se você não consegue, isso é um sinal para separar.
// Antes — asserções duplicadas / agrupadas em um único teste opaco
test('isValid', () => {
  expect(isValid('plain text')).toBe(true);
  expect(isValid('with spaces')).toBe(true);
  expect(isValid('Fritz-box')).toBe(true);
  expect(isValid('Fritz-box')).toBe(true); // duplicata
  expect(isValid('<script>')).toBe(false);
});

// Depois — uma única asserção, cada caso nomeado e reportado de forma independente
test.each([
  ['plain text', true],
  ['with spaces', true],
  ['Fritz-box',  true],   // menos é válido
  ['<script>',   false],  // rejeita marcação
])('isValid(%j) === %s', (input, expected) => {
  expect(isValid(input)).toBe(expected);
});

A linha duplicada se foi, as entradas distintas estão explícitas, e uma falha nomeia o caso exato.