Pruebas automatizadas y depuración
Una prueba ejecuta un comportamiento y verifica una propiedad repetible. Depurar usa evidencia para reducir hipótesis hasta encontrar la causa, no sólo el síntoma.
Para este tema conviene tener claro:Funciones, parámetros y alcance
Una prueba no demuestra que el programa no tiene errores. Demuestra que cierto comportamiento, bajo condiciones explícitas, produjo el resultado esperado. Su valor está en repetir esa evidencia cada vez que el código cambia.
Preparar, ejecutar y afirmar
Una prueba clara prepara datos, ejecuta una acción y afirma resultados. El nombre debería contar el escenario y la garantía, no repetir el nombre del método.
Los casos incluyen una entrada representativa, fronteras, vacío y errores esperados. Copiar muchos ejemplos similares aumenta volumen sin necesariamente cubrir otra clase de comportamiento.
Antes de seguir, predecí
Unitaria, de integración y de punta a punta
Una prueba unitaria aísla una regla pequeña y da diagnóstico rápido. Una de integración verifica que componentes reales se entiendan: serialización, base de datos o red. Una prueba de extremo a extremo recorre un flujo visible y ofrece más confianza sistémica, pero suele ser más lenta y frágil.
Ningún nivel reemplaza a los otros. Se combinan según el riesgo que cada frontera introduce.
Cuando no se sabe la salida pero sí la propiedad
No siempre conocemos una salida exacta, pero sí propiedades. Ordenar debe conservar elementos y dejarlos en orden; serializar y deserializar debería recuperar el valor; agregar una entrada a un conjunto dos veces debería equivaler a agregarla una.
Generar muchas entradas a partir de esas propiedades descubre combinaciones que los ejemplos manuales no imaginaron y obliga a expresar qué significa que la operación sea correcta.
Depurar empieza por reproducir
Depurar empieza por reproducir. Después se reduce el caso, se observa el estado cerca del primer punto donde diverge y se formula una hipótesis falsable. Logs, breakpoints y trazas sirven para medir, no para reemplazar una pregunta concreta.
Corregir donde explota puede esconder la causa original. Hay que seguir el dato inválido hacia atrás hasta la frontera que debía impedirlo y agregar una prueba que falle antes del arreglo.
Código bajo prueba
const applyDiscount = (price, percent) => {
if (percent <= 0) return price;
if (percent > 100) percent = 100;
return price - (price * percent) / 100;
};Metele un bug
El código está como lo escribió quien lo programó: toda la suite pasa.
La suite
- pasasin descuento devuelve el precioapplyDiscount(100, 0) === 100
- pasaaplica el 50 por cientoapplyDiscount(100, 50) === 50
- pasadevuelve un númerotypeof resultado === "number"
Qué hace mala a una prueba
Las pruebas deben ser deterministas y controlar reloj, azar y servicios externos. Probar detalles privados vuelve costoso refactorizar sin aumentar garantías; conviene afirmar resultados observables.
La cobertura señala código no ejecutado, pero cien por ciento no asegura buenos casos. Importa qué riesgos se ejercitan y si un error real produciría una falla comprensible.
Un test que sirve y uno que no
// No sirve: prueba que el código hace lo que hace
test('applyDiscount devuelve un número', () => {
expect(typeof applyDiscount(100, 10)).toBe('number');
});
// Sirve: fija una regla de negocio, con un caso que podría fallar
test('un descuento del 10 % sobre 100 deja el precio en 90', () => {
expect(applyDiscount(100, 10)).toBe(90);
});
// Sirve más: el borde que nadie probó a mano
test('un descuento mayor a 100 se recorta al 100 y no devuelve plata', () => {
expect(applyDiscount(100, 150)).toBe(0);
});
// El test de regresión que se escribe al arreglar un bug
test('un cupón vencido no aplica descuento (bug #482)', () => {
expect(applyDiscount(100, 10, { expiresAt: yesterday })).toBe(100);
});El primero pasa siempre, incluso con la función rota, y por eso no aporta nada más que una cifra de cobertura. Los otros tres fallan si alguien cambia el comportamiento, que es lo único que un test tiene que hacer.
El cuarto tiene un valor distinto: se escribe antes de arreglar el bug y se verifica que falle. Esa comprobación —que el test falle sin el arreglo— es lo que garantiza que el test está probando lo que uno cree.
Depurar sin adivinar
| Paso | Qué se hace | Qué error evita |
|---|---|---|
| Reproducir | encontrar la entrada mínima que falla | arreglar algo que no era |
| Bisecar | partir el espacio de causas al medio | probar cambios al azar |
| Formular | escribir la hipótesis antes de tocar nada | confundir correlación con causa |
| Verificar | un cambio por vez | no saber cuál de los tres lo arregló |
| Fijar | un test que falle sin el arreglo | que vuelva dentro de seis meses |
Cierre
Las pruebas convierten contratos en verificaciones repetibles. La depuración convierte síntomas en hipótesis y evidencia. Juntas forman un ciclo: reproducir, explicar, corregir en la causa y conservar una prueba que impida la regresión.
Autoevaluación
¿Lo entendiste?
Práctica