Prueba verbosa.
Una prueba que usa mucho más código del necesario para plantear su escenario, sepultando la única relación de causa y efecto que verifica bajo código repetitivo de preparación, datos irrelevantes y aserciones campo por campo.
##Signs and Symptoms
No puedes saber de un vistazo qué demuestra una prueba, aunque no esté ocurriendo nada complicado: la intención queda ahogada en el volumen. Indicios habituales:
- El método de prueba es largo (el catálogo de Test Smells usa un umbral aproximado de >30 líneas) o se extiende más allá de una pantalla.
- Un gran bloque
arrangeconstruye objetos campo por campo, gran parte de ellos irrelevantes para el comportamiento bajo prueba. - Muchos literales codificados a mano sin un vínculo evidente entre las entradas y las salidas afirmadas.
- El resultado se comprueba con un montón de aserciones de un solo campo en lugar de una única comparación significativa.
- Al leer las aserciones, no resulta fácil relacionar qué entrada provocó qué valor esperado.
test('places an order for a returning customer', () => {
const address = new Address();
address.street = '123 Main St';
address.city = 'Springfield';
address.zip = '00000'; // irrelevante para esta prueba
address.country = 'US';
const customer = new Customer();
customer.id = 42;
customer.firstName = 'Ada';
customer.lastName = 'Lovelace';
customer.email = 'ada@example.com';
customer.address = address;
customer.createdAt = new Date('2020-01-01'); // ruido
const product = new Product();
product.sku = 'SKU-1';
product.name = 'Widget';
product.price = 9.99;
const cart = new Cart();
cart.add(product, 2);
const order = orderService.placeOrder(customer, cart);
expect(order.status).toBe('CONFIRMED'); // las únicas líneas que importan
expect(order.lineItems.length).toBe(1);
expect(order.lineItems[0].sku).toBe('SKU-1');
expect(order.lineItems[0].quantity).toBe(2);
expect(order.total).toBe(19.98);
expect(order.customerId).toBe(42);
});
Alrededor de 25 líneas de construcción custodian una aserción de dos líneas sobre totales y estado.
##Reasons for the Problem
Por qué ocurre
- Construcción en línea con campos obligatorios. Construir objetos reales mediante constructores/setters te obliga a aportar datos que a la prueba no le importan, solo para que el código compile y se ejecute. Meszaros señala que el nivel de detalle necesario para hacer ejecutable una prueba puede volverla «tan verbosa que resulta difícil de entender».
- Crecimiento por copiar y pegar. Una prueba empieza siendo pequeña y luego cada nuevo escenario se crea copiando el anterior y ajustando un valor, así que el ruido se acumula.
- Sin ayudantes de construcción compartidos. Sin métodos de creación (Creation Methods), constructores (builders) ni fixtures, cada prueba vuelve a declarar el grafo de objetos completo.
- Datos codificados a mano y verificación campo por campo. Escribir literales por todas partes y hacer aserciones sobre cada propiedad parece «exhaustivo», pero multiplica el número de líneas.
Por qué resulta perjudicial
- Legibilidad. La Prueba verbosa es una variante de la Prueba oscura: el catálogo incluso indica «Prueba oscura» como su alias. Quien lee no puede ver la relación de causa y efecto entre el fixture y el resultado, que es el sentido mismo de una prueba basada en ejemplos.
- Mantenibilidad. Una prueba de más de 30 líneas tiende a cargar con «varias responsabilidades», de modo que un pequeño cambio en producción se propaga a muchas líneas a lo largo de muchas pruebas. La preparación en línea masiva también genera duplicación.
- Falsa confianza. Las pruebas largas y ruidosas ocultan errores a plena vista: un literal incorrecto o una aserción mal colocada pasan fácilmente desapercibidos, y la verbosidad hace que los revisores lean por encima. El volumen aparenta rigor sin demostrar nada más.
- Fiabilidad. Cuanto más estado irrelevante fija una prueba, más probable es que se rompa por motivos ajenos a su intención (sobreespecificación), produciendo fallos frágiles y de baja señal.
##Treatment
Saca el ruido fuera del cuerpo de la prueba para que solo queden visibles las variables que determinan el comportamiento. Acciones concretas (todas de xUnit Test Patterns):
- Extrae métodos de creación (Creation Methods) / usa un constructor (builder). Sustituye la construcción de objetos en línea por un ayudante con un nombre evocador que aporte valores por defecto razonables; pásale solo los valores que de verdad le importan a la prueba (método de creación parametrizado). Esto elimina tanto el código repetitivo como la información irrelevante.
- Asigna por defecto los datos irrelevantes dentro del ayudante, señalando que «estos valores no afectan al resultado».
- Sustituye las comprobaciones campo por campo por un objeto esperado (Expected Object) (
toEqual/toMatchObject) o una aserción personalizada / método de verificación que nombre el concepto que se está verificando. - Verifica un solo comportamiento por prueba. Si una sola prueba es larga porque ejercita varios resultados (una Prueba ansiosa, Eager Test), divídela.
- Parametriza las pruebas verbosas casi idénticas con
test.each/it.eachen lugar de copiar y pegar.
// antes — véase Indicios y síntomas (≈30 líneas de preparación + 6 aserciones)
// después
test('confirms the order and charges line-item total', () => {
const customer = aCustomer(); // Método de creación, valores por defecto
const cart = aCartWith(aProduct({ price: 9.99 }), 2); // solo la entrada relevante
const order = orderService.placeOrder(customer, cart);
expect(order).toMatchObject({ // Objeto esperado, no campo por campo
status: 'CONFIRMED',
total: 19.98,
});
});
El escenario ahora se lee en segundos: un cliente compra dos unidades de un producto de 9,99 $ → el pedido se confirma con un total de 19,98 $. Los ayudantes (aCustomer, aProduct, aCartWith) son utilidades compartidas y probadas, así que el ruido vive en un solo lugar en lugar de en cada prueba.
Salvaguarda: limita en tu linter la longitud de las funciones de prueba y el número de aserciones (véanse los detectores) para que las pruebas no vuelvan a crecer en silencio hasta convertirse en Pruebas verbosas.
##Detected by
- eslint max-lines-per-function — Impone un número máximo de líneas por función: señala los cuerpos de prueba que superan el límite configurado, la medida central de una Prueba verbosa.
- eslint max-statements — Impone un número máximo de sentencias en un bloque de función: limita cuánto puede hacer una única prueba (un callback de función o de flecha).
- sonar S138 — Las funciones no deberían tener demasiadas líneas de código: se aplica a las funciones de prueba; señala los métodos de prueba sobredimensionados y con múltiples responsabilidades (RSPEC-138).
- eslint-jest jest/max-expects — Impone un número máximo de llamadas a expect() por prueba (5 por defecto): detecta la faceta de acumulación de aserciones de las pruebas verbosas/ansiosas.
- eslint-vitest vitest/max-expects — Impone un número máximo de llamadas a expect() por prueba (5 por defecto): equivalente de Vitest a jest/max-expects.