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
- 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.
- 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.eachen Jest/Vitest,@ParameterizedTesten JUnit 5). Cada fila se reporta y se nombra por separado, de modo que los fallos señalan con precisión la entrada culpable. - 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). - 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.