4.4Validación, gestión de errores y tests (Jest)
Lo esencial en 30 segundos
- No confiar nunca en las entradas : validar (Zod/Joi) antes de tratar, rechazar en 400 claro — contra datos corruptos, bugs y fallos.
- Centralizar los errores : middleware Express (err, req, res, next) en último, try/catch sobre los await, 404/400 esperados vs 500 inesperados.
- Reflejo de Salim : testear con Jest (unitario + ruta) y relanzar tras cada modificación — la no-regresión que libera.
Mini-caso hilado — Salim fiabiliza antes de la puesta en producción
La API de Salim (4.1-4.3) funciona en demo, pero un vendedor crea un producto sin nombre, una ruta falla sobre un id desconocido y nadie sabe qué testear antes de cada entrega. Salim añade un esquema Zod que rechaza los cuerpos inválidos en 400, un middleware de errores centralizado que responde limpiamente sin filtrar detalles, luego escribe sus primeros tests Jest : una función testeada aisladamente, una ruta testeada de extremo a extremo. En la próxima modificación, relanza la suite en pocos segundos. Último hito del hilo : del prototipo a la aplicación profesional.
Último episodio del caso 4.1-4.3 — Salim cierra el nivel : construir, asegurar, animar, fiabilizar.
1. La validación de las entradas
Recordatorio de 4.2 : no confiar nunca en las entradas. Antes de tratar los datos de un cliente (cuerpo, parámetros), verifique que tienen el formato esperado : campos requeridos presentes, tipos correctos, restricciones respetadas (email válido, longitud máxima). Si son inválidos : rechazo en 400 con un mensaje claro, antes de todo tratamiento. Librerías : Zod (moderno, excelente con TypeScript — un esquema que valida y tipa) o Joi (probado). La validación protege contra los datos corruptos, los bugs y ciertos fallos — indispensable para toda entrada externa.
2. La gestión de errores centralizada
En producción, ocurren errores : base no disponible, recurso no encontrado, operación fallida. En Express, el patrón es un middleware de gestión de errores centralizado — función especial de 4 parámetros (err, req, res, next), colocada en último : los errores se dirigen ahí y devuelve una respuesta coherente (código correcto, mensaje claro, sin detalles sensibles). Se rodean los await con try/catch (o wrapper) y se distinguen los errores esperados (404, 400) de los inesperados (500).
3. Los tests automatizados con Jest
En vez de verificar a mano (fastidioso, incompleto, no repetible), se escribe código que testea el código. Jest es el framework más popular en Node : unitarios (una función aislada), integración (varias partes, una ruta con su base), de extremo a extremo. Un test verifica que una entrada da la salida esperada (expect(resultat).toBe(attendu)). Beneficios : confianza, no-regresión (relanzar tras cada cambio), documentación del comportamiento, mejor diseño (el código testeable está mejor estructurado). Validación + errores + tests : lo que separa un prototipo de una app profesional.
🔎 Ejemplo concreto — 🔎 Ejemplo concreto — Validar y luego testear
Los dos gestos de Salim sobre la creación de producto :
Esquema : { nom: cadena no vacía, prix: número positivo }
→ cuerpo inválido : 400 { erreur: 'nom requerido, prix positivo esperado' }
Test : POST /produits sin nom → se espera 400
Test : función total(panier) → expect(total).toBe(300)
Lectura : el esquema rechaza antes del tratamiento, el test bloquea el comportamiento.
Lección : 400 claro para el cliente, tests en verde para el equipo.
Vocabulario de la sección
- Validación de las entradas
- Verificar que los datos recibidos tienen el formato esperado (campos, tipos, restricciones) antes del tratamiento ; rechazar en 400 si son inválidos. Herramientas : Zod, Joi.
Ejemplo sencillo: El esquema de Salim rechaza un producto sin nombre antes de toda escritura.
Lo que no es: tratar directamente req.body sin ningún control.
Reflejo: ninguna entrada externa toca los datos sin esquema.
- Middleware de errores
- Función Express especial (err, req, res, next) colocada en último, que centraliza los errores y devuelve una respuesta coherente.
Ejemplo sencillo: En casa de Salim, todo error termina ahí : código correcto, mensaje claro, sin fuga sensible.
Lo que no es: try/catch dispersos que devuelven cada uno un formato distinto.
Reflejo: un solo formato de error para toda la API.
- Esperado vs inesperado
- Esperados : 404 no encontrado, 400 inválido — gestionados explícitamente. Inesperados : bugs → 500 sin detalles sensibles.
Ejemplo sencillo: Salim devuelve 404 para un producto desconocido, 500 genérico para un bug.
Lo que no es: una stack trace en bruto devuelta al cliente en producción.
Reflejo: el cliente recibe el código correcto, nunca nuestros secretos.
- Test automatizado (Jest)
- Código que verifica el código : entrada dada → salida esperada (expect().toBe()). Unitarios, integración, de extremo a extremo.
Ejemplo sencillo: Salim testea su función de total y luego su ruta POST.
Lo que no es: una verificación manual única en el navegador.
Reflejo: escribir el test que prueba que funciona.
- No-regresión
- Relanzar los tests tras cada modificación para detectar lo que se rompió sin querer.
Ejemplo sencillo: Salim relanza su suite antes de cada entrega y duerme tranquilo.
Lo que no es: descubrir la rotura en producción por un ticket cliente.
Reflejo: suite en verde exigida antes de cada merge.
En la práctica — En práctica — Fiabilizar una API (validación, errores, tests)
- Valide las entradas de una ruta con Zod o Joi : defina un esquema, rechace lo inválido en 400 con un mensaje claro.
- Ponga el middleware de errores centralizado (err, req, res, next) en último ; rodee los await con try/catch.
- Distinga errores esperados (404, 400) e inesperados (500) ; no divulgue ningún detalle sensible en producción.
- Escriba tests Jest : un unitario (expect().toBe()) y uno de ruta ; reláncelos tras cada modificación.
Guion listo para decir — anunciar la fiabilización (30 s)
« Soy Salim : antes de la puesta en producción, cada entrada es validada por esquema con rechazo en 400 claro, todos los errores pasan por un middleware centralizado que no filtra ningún detalle, y la suite Jest cubre lo unitario como las rutas — relanzada en cada modificación para la no-regresión. »
Para decir antes de cada puesta en producción ; muestre la suite en verde.
Modelo copiar-pegar — validación Zod + errores + test Jest (código probado)
// 1) Validación (npm i zod) + 2) errores centralizados
const express = require('express');
const { z } = require('zod');
const app = express();
app.use(express.json());
const schemaProduit = z.object({
nom: z.string().min(1, 'nom requerido'),
prix: z.number().positive('prix positivo esperado')
});
const produits = [];
app.post('/api/v1/produits', (req, res, next) => {
try {
const donnees = schemaProduit.parse(req.body); // lanza si es inválido
const produit = { id: produits.length + 1, ...donnees };
produits.push(produit);
res.status(201).json(produit);
} catch (err) {
err.statut = 400; // esperado : entrada inválida
next(err);
}
});
// Middleware de errores : SIEMPRE en último
// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
const statut = err.statut || 500;
const message = statut === 500 ? 'Error interno' : err.message;
res.status(statut).json({ erreur: message });
});
module.exports = app;
// 3) Test Jest (archivo app.test.js, npm i -D jest supertest) :
// const request = require('supertest');
// const app = require('./app');
// test('POST sin nom → 400', async () => {
// const res = await request(app).post('/api/v1/produits').send({ prix: 10 });
// expect(res.status).toBe(400);
// });
// test('POST válido → 201', async () => {
// const res = await request(app).post('/api/v1/produits').send({ nom: 'Couscoussier', prix: 2500 });
// expect(res.status).toBe(201);
// expect(res.body.nom).toBe('Couscoussier');
// });
Prototipo de aprendizaje
Validación mínima + 2-3 tests sobre lo crítico : formador sin frenar la exploración.
API real en producción
Como Salim : esquema sobre cada entrada externa, middleware centralizado, suite Jest exigida en verde antes del merge.
Proyecto TypeScript
Zod valida Y tipa : un solo esquema para la seguridad runtime y la robustez compile-time.
Equipo que descubre los tests
Empezar por las rutas críticas (creación, conexión) : valor inmediato, hábito instalado.
✓ Buenas prácticas
- Validar antes de tratar, rechazar en 400 claro.
- Centralizar los errores en un middleware único.
- Testear y relanzar tras cada modificación.
✗ Errores frecuentes
- Tratar req.body sin validación.
- Devolver la stack trace al cliente.
- Solo testear a mano, una vez.
Puntos clave
- VALIDACIÓN (Zod/Joi) : verificar formato/tipos/restricciones ANTES del tratamiento, 400 claro si es inválido. No confiar nunca en el cliente.
- ERRORES centralizados : middleware (err, req, res, next) en último → respuesta coherente ; gestionar el ASYNC (try/catch).
- TESTS JEST : unitarios + integración (ruta). expect(resultat).toBe(attendu).
- Beneficios : CONFIANZA, NO-REGRESIÓN, documentación, mejor diseño. El paso prototipo → profesional.
Reto 48h — aplica mañana
Añada un esquema de validación a su ruta más crítica, el middleware de errores, y 2 tests Jest (1 rechazo 400, 1 éxito) — suite en verde exigida.
- Puedo explicar validación, middleware de errores, Jest.
- Sé escribir un 400 claro y un 500 sin fuga.
- Tengo 2 tests en verde sobre mi ruta crítica.
Preguntas frecuentes
¿Por qué testear cuando « ya funciona »?
Porque « funciona » solo vale para los casos verificados a mano, hoy. Mañana, cada modificación puede romper silenciosamente otra cosa (regresión) — y sin tests, habría que volver a verificarlo todo a mano cada vez, lo que nunca se hace. Los tests aportan confianza (se sabe, no se cree), no-regresión (alerta inmediata tras cada cambio), documentación del comportamiento esperado, y empujan a estructurar mejor el código testeable.
Validación, errores, tests : ¿hace falta todo, para todos los proyectos?
Dose según lo que esté en juego. Prototipo desechable : aligere para ir rápido, pero practique un mínimo (buenos hábitos). Aplicación real (usuarios, producción, equipo) : indispensable — validación (datos corruptos, bugs, fallos), errores (si no el servidor falla o filtra detalles), tests (si no las regresiones pasan hasta producción). Estas tres prácticas transforman un prototipo en aplicación fiable.
¿Dónde colocar el middleware de gestión de errores Express?