ConstructiCat Logo
CodeBust.
Browse section ▾

Aserción Duplicada.

Un único método de prueba verifica la misma condición más de una vez —repitiendo una aserción idéntica o volviendo a comprobar lógica equivalente— en lugar de eliminar la comprobación redundante o dividir los casos distintos en sus propias pruebas enfocadas.

##Signs and Symptoms

Ves que la misma aserción aparece dos veces (mismo matcher, mismos argumentos) en una prueba, o varios bloques de aserción copiados y pegados que difieren solo en valores literales y vuelven a probar el mismo comportamiento. Señales reveladoras:

  • Una línea de aserción literalmente idéntica aparece dos o más veces en el cuerpo.
  • Muchas llamadas expect(...)/assertEquals(...) que ejercitan la misma condición con entradas diferentes, todas amontonadas en un método cuyo nombre describe solo un único escenario.
  • Aserciones sobrantes de la depuración ("déjame comprobar también X") que nunca se limpiaron.
  • El nombre de la prueba (p. ej. testXmlSanitizer) no da ninguna pista sobre cuál de sus muchas comprobaciones falló.
test('sanitizer accepts valid input', () => {
  expect(isValid('plain text')).toBe(true);
  expect(isValid('with spaces')).toBe(true);
  expect(isValid('Fritz-box')).toBe(true);   // "el menos es válido"
  expect(isValid('Fritz-box')).toBe(true);   // <-- duplicado exacto, no aporta nada
  expect(isValid('<script>')).toBe(false);
});

La línea duplicada Fritz-box es la Aserción Duplicada canónica; el olor más amplio es que un método agrupa silenciosamente muchas comprobaciones de la misma condición bajo un solo nombre.

##Reasons for the Problem

Por qué ocurre

  • Copiar y pegar. Se duplica un bloque de aserción y se cambian los literales (o incluso nada).
  • Residuo de depuración. Quedan olvidadas aserciones adicionales que se añadieron para sondear el comportamiento.
  • Agrupación. Los desarrolladores prueban "un método" amontonando todos los casos en una sola prueba en lugar de dividirlos, produciendo comprobaciones repetidas y casi idénticas.

Por qué resulta perjudicial

  • Falsa confianza. Una aserción duplicada verdaderamente idéntica añade cero cobertura — nunca puede fallar cuando su gemela pasa — y, aun así, hace que la prueba parezca más exhaustiva de lo que es.
  • Diagnóstico de fallos difícil. Cuando varias aserciones con la misma forma comparten un método, el informe de fallo apunta al método, no a la entrada concreta que se rompió. Peor aún, una prueba con configuración por defecto se detiene en la primera aserción que falla, así que las comprobaciones duplicadas posteriores nunca se ejecutan — arreglas una, vuelves a ejecutar, te topas con la siguiente (se solapa con la Ruleta de Aserciones).
  • Mala legibilidad. El lector no puede saber si la repetición es intencional (casos distintos) o un error (duplicado real), y el nombre de la prueba documenta solo una de muchas condiciones.
  • Coste de mantenimiento. Cambia el comportamiento bajo prueba y tendrás que rastrear y actualizar cada aserción duplicada; si te saltas una, la suite se vuelve inconsistente.

##Treatment

  1. Elimina los duplicados exactos. Si una aserción es idéntica byte a byte a otra de la misma prueba, elimínala — es peso muerto, no cobertura.
  2. Parametriza los casos equivalentes. Cuando los "duplicados" son en realidad la misma condición con entradas diferentes, conviértelos en una prueba de tabla/parametrizada (test.each en Jest/Vitest, @ParameterizedTest en JUnit 5). Cada fila se reporta y se nombra por separado, de modo que los fallos señalan con precisión la entrada culpable.
  3. Divide las condiciones genuinamente diferentes en pruebas separadas, cada una con un nombre que indique lo que verifica (accepts hyphenated host names, rejects script tags).
  4. Nombra según la intención. El nombre de una prueba debería describir un único comportamiento; si no puedes, esa es una señal para dividir.
// Antes — aserciones duplicadas / agrupadas en una prueba opaca
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); // duplicado
  expect(isValid('<script>')).toBe(false);
});

// Después — una aserción, cada caso nombrado y reportado de forma independiente
test.each([
  ['plain text', true],
  ['with spaces', true],
  ['Fritz-box',  true],   // el menos es válido
  ['<script>',   false],  // rechaza el marcado
])('isValid(%j) === %s', (input, expected) => {
  expect(isValid(input)).toBe(expected);
});

La fila duplicada ha desaparecido, las entradas distintas son explícitas y un fallo nombra el caso exacto.