Atlasingeniería

Code review en la prácticaTests que pasan sin probar nadaTema 3

Todo el flujo en un solo caso: cuando falla no se sabe dónde

Un test de doscientas líneas que ejercita el flujo completo y termina con quince comprobaciones. Cuando falla, el mensaje dice poco y hay que leerlo entero. Cómo partirlo sin perder cobertura.

Un solo caso de prueba que arma el escenario, ejecuta el flujo completo y hace quince comprobaciones al final. Cubre mucho y se siente completo.

Después falla en el pipeline y el mensaje dice que se esperaba true y se obtuvo false. La pregunta “¿qué se rompió?” cuesta veinte minutos, y ese costo se paga cada vez.

Qué se pierde al juntar todo

ConsecuenciaCómo se nota
El fallo no localizaHay que leer todo el test para saber en qué paso estaba
La primera comprobación que falla oculta el restoSe arregla una cosa, vuelve a fallar, se arregla otra: tres vueltas de pipeline
El nombre no dice nada«funciona el checkout» no aporta información cuando aparece en rojo
Se vuelve frágilCualquier cambio en cualquiera de los pasos lo rompe, aunque no toque lo que verifica
El segundo es el que más tiempo consume en la práctica.

Y hay un efecto cultural: un test largo que falla seguido por motivos ajenos es el primer candidato a que alguien lo marque como omitido “por ahora”. Un test omitido es peor que no tenerlo, porque figura en la lista y da tranquilidad falsa.

Cómo se parte

Partir no significa repetir el escenario quince veces. Significa separar la preparación de las verificaciones.

La estructura que funciona

  1. Un escenario compartido, armado en la preparación del grupo: los datos, el estado inicial, la ejecución del flujo.
  2. Un caso por comportamiento verificado. Cada uno con una o dos comprobaciones y un nombre que describa la regla: «no permite avanzar sin medio de pago», «descuenta el cupón una sola vez».
  3. Nombres que se lean en el reporte. El nombre del test es el mensaje de error que uno va a ver; si dice la regla, muchas veces no hace falta abrir el archivo.
  4. Sin dependencias entre casos. Que el orden de ejecución no importe: un test que necesita que otro haya corrido antes es un test que va a fallar solo cuando se ejecuten en paralelo.

La preparación que se vuelve el problema

Cuando el escenario necesita treinta líneas para existir, el test es difícil de leer y el diseño está avisando algo.

Antes de seguir, predecí

Un test necesita construir un objeto con veinte campos, de los cuales sólo dos importan para lo que verifica. ¿Qué conviene?

Y si la preparación es enorme porque el componente necesita medio sistema para funcionar, eso no es un problema del test: es acoplamiento. El test lo está reportando.

Qué nivel de test corresponde

Parte de estos casos gigantes existen porque se usó un test de extremo a extremo para verificar algo que era lógica pura.

La regla práctica

  1. Lógica de negocio: test unitario, rápido, sin dependencias. Ahí van todos los casos de borde.
  2. Integración entre piezas propias: unos pocos, verificando que el contrato se respeta.
  3. Extremo a extremo: los caminos críticos y nada más. Son lentos, frágiles y caros de mantener; su valor es confirmar que el sistema está enchufado, no cubrir combinaciones.
Más a fondo · nivel seniorEl test largo que sí vale la pena

Hay una excepción legítima: el test que documenta un flujo completo de negocio, escrito como narrativa, para que alguien entienda cómo se usa el sistema. Ése puede ser largo, y lo que lo hace aceptable es que sea uno solo, explícitamente pensado como documentación, y que la cobertura real de los casos esté en los tests chicos. El problema no es la longitud: es que un test largo sea el único lugar donde se verifica una regla.

El test grande contra los chicos

Código bajo prueba

const orderTotal = (items, coupon, shipping) => {
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const discount = coupon ? subtotal * coupon.rate : 0;
const shippingCost = subtotal - discount > 5000 ? 0 : shipping;
return subtotal - discount + shippingCost;
};

Metele un bug

El código está como lo escribió quien lo programó: toda la suite pasa.

La suite

  • pasacalcula el total de un pedido completo15 comprobaciones sobre subtotal, descuento, envío y total
  • pasael subtotal suma precio por cantidadorderTotal([{price: 100, quantity: 2}], null, 0) === 200
  • pasael cupón descuenta su porcentajeorderTotal([{price: 100, quantity: 1}], {rate: 0.1}, 0) === 90
  • pasaarriba de 5000 el envío es gratisorderTotal([{price: 6000, quantity: 1}], null, 800) === 6000

Probá las cuatro mutaciones. El test grande se pone en rojo con todas, y eso suena bien hasta que lo mirás de cerca: rojo con todas significa que nunca te dice cuál. Los tres chicos se encienden de a uno, y ahí el mensaje de falla ya es el diagnóstico.

Cuántos tests y de qué tipo

Cierre

Autoevaluación

¿Lo entendiste?

¿Cuál es el costo práctico de juntar quince comprobaciones en un solo test?
Un test necesita treinta líneas de preparación. ¿Qué está indicando?