Aislar con dobles de prueba
Para probar una unidad sin sus dependencias reales (red, BD, reloj, pagos) usamos dobles de prueba (test doubles):
- Stub: devuelve respuestas predefinidas. Sustituye una dependencia para que
devuelva siempre lo que el test necesita (
obtenerUsuario() => { id: 1 }). - Spy (espía): envuelve una función real o falsa y registra cómo se llamó (cuántas veces, con qué argumentos). Sirve para verificar interacciones.
- Mock: un doble con expectativas programadas: ya sabe qué llamadas debe recibir y falla si no se cumplen. Combina stub (responde) y spy (verifica).
En la práctica el lenguaje es laxo: mucha gente llama "mock" a cualquier doble. Lo importante es la intención: stub = controlar entradas, spy/mock = verificar salidas/interacciones.
Inyección de dependencias
Para poder sustituir una dependencia por un doble, la función no debe crearla por dentro, debe recibirla. Eso es inyección de dependencias:
// Difícil de testear: la dependencia está "soldada".
function registrar(email) { enviarCorreoReal(email); }
// Fácil de testear: le inyectamos el colaborador.
function registrar(email, enviarCorreo) { enviarCorreo(email); }
// En el test pasamos un espía y verificamos que se llamó.
Probar funciones puras y endpoints
- Funciones puras (misma entrada → misma salida, sin efectos): las más fáciles. Solo afirmas sobre el valor devuelto.
- Endpoints HTTP: un test de integración levanta el handler y le envía una petición. En Node se usa supertest sobre la app de Express:
// Conceptual (supertest no corre en este sandbox):
await request(app).get("/usuarios/1").expect(200);
En estos ejercicios simulamos esa petición/respuesta con crearReq() y
crearRes(), y afirmamos sobre res.statusCode y res.body.
Aserciones (expect)
Una aserción declara lo que debe cumplirse; si no, lanza y el test falla.
La API expect(actual).toBe(esperado) es solo azúcar sobre un if + throw.
Cobertura y sus límites
La cobertura (coverage) mide qué porcentaje de líneas/ramas ejecutó la suite. Útil para encontrar zonas sin probar, pero no mide calidad: puedes tener 100% de cobertura y cero aserciones útiles. Cobertura alta ≠ código correcto.
Ejemplos
Un espía hecho a mano
function crearEspia() {
const espia = (...args) => { espia.llamadas.push(args); };
espia.llamadas = [];
return espia;
}
const enviar = crearEspia();
function registrar(email, enviarCorreo) { enviarCorreo(email); }
registrar("ana@x.com", enviar);
console.log(enviar.llamadas.length === 1); // true
console.log(enviar.llamadas[0][0] === "ana@x.com"); // true