Si vas a escribir pocas pruebas, escribí estas

Leonardo Gurgitano4 minLeer en inglés

Estructura de hormigón con pocas piezas sosteniendo el conjunto.
Estructura de hormigón con pocas piezas sosteniendo el conjunto.

Todos sabemos que habría que escribir pruebas. Casi nadie tiene el tiempo de escribir todas las que “corresponderían”, y el consejo habitual —apuntar a un porcentaje alto de cobertura— es justamente el que hace que la gente abandone, porque suena a un trabajo enorme y sin final.

Cambiemos la pregunta. Si solo pudieras escribir diez pruebas en todo el proyecto, ¿cuáles?

La respuesta es bastante clara, y casi ninguna es la que se escribe primero.

Primero: las que no sirven

Es útil sacárselas de encima, porque son las que más se escriben.

Las que prueban que el lenguaje funciona.

test('el usuario tiene nombre', () => {
  const u = new Usuario({ nombre: 'Ana' });
  expect(u.nombre).toBe('Ana');
});

Eso prueba que asignar una propiedad funciona. Ya lo sabemos.

Las que repiten la implementación.

test('calcula el total', () => {
  expect(calcularTotal(100, 0.21)).toBe(100 * 1.21);
});

Si el código tiene el error, la prueba tiene el mismo error. Escribí el resultado esperado como número: 121. Si no sabés cuánto tiene que dar, la prueba no está verificando nada.

Las que prueban lo que ya garantiza el compilador. Si tu función recibe un number, no hace falta probar qué pasa si le pasás un texto.

Las cuatro que sí

1. El error que ya te pasó

Es la de mejor relación entre esfuerzo y beneficio, y no requiere ninguna decisión: cada vez que arreglás un error, escribí una prueba que lo reproduzca antes de arreglarlo.

test('un pedido sin artículos no rompe el cálculo de envío', () => {
  expect(calcularEnvio({ articulos: [] })).toBe(0);
});

Esa prueba tiene dos virtudes. Ya sabés que el caso es real, porque pasó. Y evita que vuelva, que es lo que efectivamente ocurre con los errores en el código que más se toca.

Si adoptás una sola costumbre de esta lista, que sea esta.

2. Las reglas del negocio con dinero, fechas o permisos

Son las que hacen daño de verdad cuando fallan, y las que tienen más casos límite.

test('el descuento no puede dejar el total negativo', () => {
  expect(aplicarDescuento(50, 80)).toBe(0);
});

test('una suscripción vencida ayer ya no da acceso', () => {
  const ayer = new Date(Date.now() - 86400000);
  expect(tieneAcceso({ venceEl: ayer })).toBe(false);
});

Un error de estilo se ve. Un descuento mal calculado se factura mal durante meses hasta que alguien lo nota.

3. Una prueba que recorra el camino principal completo

Una sola, de punta a punta, del flujo que sostiene tu producto: registrarse, comprar, publicar. No de una función: de todo el camino.

test('un usuario puede comprar', async () => {
  const usuario = await registrar('ana@ejemplo.com');
  const carrito = await agregarAlCarrito(usuario, 'producto-1');
  const pedido = await finalizarCompra(carrito, tarjetaDePrueba);

  expect(pedido.estado).toBe('confirmado');
  expect(await contarPedidos(usuario)).toBe(1);
});

Es más lenta y más molesta de mantener que las otras. Y es la única que detecta que dos partes que funcionan bien por separado dejaron de entenderse entre sí, que es de donde salen las fallas más caras.

Una alcanza. Si tenés dos flujos que sostienen el producto, dos.

4. Los bordes de lo que recibís de afuera

Todo lo que entra desde fuera de tu programa puede venir de cualquier forma: lo que escribe un usuario, lo que devuelve una API, lo que hay en un archivo.

test('acepta un correo con mayúsculas y espacios alrededor', () => {
  expect(normalizarCorreo('  Ana@Ejemplo.COM ')).toBe('ana@ejemplo.com');
});

test('si la API no devuelve el campo precio, no rompe', () => {
  expect(() => procesarProducto({ nombre: 'x' })).not.toThrow();
});

Cómo saber si una prueba vale la pena

Una sola pregunta: si esto se rompe y nadie se entera, ¿qué pasa?

  • Se factura mal → escribila.
  • Alguien ve datos de otro → escribila, ya.
  • Un botón queda de otro color → no.

Y una segunda, para las que ya tenés: ¿esta prueba falló alguna vez por un error real? Una prueba que nunca falló, o que solo falla cuando cambiás el código a propósito, está costando mantenimiento sin dar nada.

Sobre el porcentaje de cobertura

Es la métrica más usada y la que peor mide lo que importa. Se puede tener un 90 % ejecutando todas las líneas sin verificar nada:

test('no rompe', () => {
  procesarPedido(pedidoDeEjemplo);   // sin un solo expect
});

Esa prueba suma cobertura y no comprueba absolutamente nada.

Diez pruebas bien elegidas valen más que doscientas automáticas. Y como beneficio secundario, diez pruebas corren en dos segundos, así que las vas a ejecutar. Doscientas tardan tres minutos y terminás ejecutándolas nunca.

Comentarios