4.4Validation, gestion d'erreurs & tests (Jest)
L'essentiel en 30 secondes
- Ne jamais faire confiance aux entrées : valider (Zod/Joi) avant de traiter, rejeter en 400 clair — contre données corrompues, bugs et failles.
- Centraliser les erreurs : middleware Express (err, req, res, next) en dernier, try/catch sur les await, 404/400 attendus vs 500 inattendus.
- Réflexe de Salim : tester avec Jest (unitaire + route) et relancer après chaque modification — la non-régression qui libère.
Mini-cas filé — Salim fiabilise avant la mise en production
L'API de Salim (4.1-4.3) marche en démo, mais un vendeur crée un produit sans nom, une route plante sur un id inconnu et personne ne sait quoi tester avant chaque livraison. Salim ajoute un schéma Zod qui rejette les corps invalides en 400, un middleware d'erreurs centralisé qui répond proprement sans fuiter de détails, puis écrit ses premiers tests Jest : une fonction testée isolément, une route testée de bout en bout. À la prochaine modification, il relance la suite en quelques secondes. Dernier jalon du fil rouge : du prototype à l'application professionnelle.
Dernier épisode du cas 4.1-4.3 — Salim boucle le niveau : construire, sécuriser, animer, fiabiliser.
1. La validation des entrées
Rappel de 4.2 : ne jamais faire confiance aux entrées. Avant de traiter les données d'un client (corps, paramètres), vérifiez qu'elles ont le format attendu : champs requis présents, types bons, contraintes respectées (email valide, longueur maximale). Si invalides : rejet en 400 avec un message clair, avant tout traitement. Bibliothèques : Zod (moderne, excellent avec TypeScript — un schéma qui valide et type) ou Joi (éprouvé). La validation protège contre les données corrompues, les bugs et certaines failles — indispensable pour toute entrée externe.
2. La gestion d'erreurs centralisée
En production, des erreurs surviennent : base indisponible, ressource introuvable, opération échouée. En Express, le pattern est un middleware de gestion d'erreurs centralisé — fonction spéciale à 4 paramètres (err, req, res, next), placée en dernier : les erreurs y sont dirigées et il renvoie une réponse cohérente (bon code, message clair, sans détails sensibles). On entoure les await de try/catch (ou wrapper) et on distingue les erreurs attendues (404, 400) des inattendues (500).
3. Les tests automatisés avec Jest
Plutôt que vérifier à la main (fastidieux, incomplet, non répétable), on écrit du code qui teste le code. Jest est le framework le plus populaire en Node : unitaires (une fonction isolée), intégration (plusieurs parties, une route avec sa base), bout en bout. Un test vérifie qu'une entrée donne la sortie attendue (expect(resultat).toBe(attendu)). Bénéfices : confiance, non-régression (relancer après chaque changement), documentation du comportement, meilleure conception (le code testable est mieux structuré). Validation + erreurs + tests : ce qui sépare un prototype d'une app professionnelle.
🔎 Exemple concret — 🔎 Exemple concret — Valider puis tester
Les deux gestes de Salim sur la création de produit :
Schéma : { nom: chaîne non vide, prix: nombre positif }
→ corps invalide : 400 { erreur: 'nom requis, prix positif attendu' }
Test : POST /produits sans nom → on attend 400
Test : fonction total(panier) → expect(total).toBe(300)
Lecture : le schéma rejette avant traitement, le test verrouille le comportement.
Leçon : 400 clair pour le client, tests verts pour l'équipe.
Vocabulaire de la section
- Validation des entrées
- Vérifier que les données reçues ont le format attendu (champs, types, contraintes) avant traitement ; rejeter en 400 si invalides. Outils : Zod, Joi.
Exemple simple : Le schéma de Salim refuse un produit sans nom avant toute écriture.
Ce que ce n'est pas : traiter directement req.body sans aucun contrôle.
Réflexe : aucune entrée externe ne touche les données sans schéma.
- Middleware d'erreurs
- Fonction Express spéciale (err, req, res, next) placée en dernier, qui centralise les erreurs et renvoie une réponse cohérente.
Exemple simple : Chez Salim, toute erreur finit là : bon code, message clair, sans fuite sensible.
Ce que ce n'est pas : des try/catch dispersés renvoyant chacun un format différent.
Réflexe : un seul format d'erreur pour toute l'API.
- Attendu vs inattendu
- Attendues : 404 introuvable, 400 invalide — gérées explicitement. Inattendues : bugs → 500 sans détails sensibles.
Exemple simple : Salim renvoie 404 pour un produit inconnu, 500 générique pour un bug.
Ce que ce n'est pas : une stack trace brute renvoyée au client en production.
Réflexe : le client reçoit le bon code, jamais nos secrets.
- Test automatisé (Jest)
- Code qui vérifie le code : entrée donnée → sortie attendue (expect().toBe()). Unitaires, intégration, bout en bout.
Exemple simple : Salim teste sa fonction de total puis sa route POST.
Ce que ce n'est pas : une vérification manuelle unique dans le navigateur.
Réflexe : écrire le test qui prouve que ça marche.
- Non-régression
- Relancer les tests après chaque modification pour détecter ce qu'on a cassé sans le vouloir.
Exemple simple : Salim relance sa suite avant chaque livraison et dort tranquille.
Ce que ce n'est pas : découvrir la casse en production par un ticket client.
Réflexe : suite verte exigée avant chaque merge.
En pratique — En pratique — Fiabiliser une API (validation, erreurs, tests)
- Validez les entrées d'une route avec Zod ou Joi : définissez un schéma, rejetez l'invalide en 400 avec un message clair.
- Mettez en place le middleware d'erreurs centralisé (err, req, res, next) en dernier ; entourez les await de try/catch.
- Distinguez erreurs attendues (404, 400) et inattendues (500) ; ne divulguez aucun détail sensible en production.
- Écrivez des tests Jest : un unitaire (expect().toBe()) et un de route ; relancez-les après chaque modification.
Script prêt à dire — annoncer la fiabilisation (30 s)
« C'est Salim : avant la mise en production, chaque entrée est validée par schéma avec rejet en 400 clair, toutes les erreurs passent par un middleware centralisé qui ne fuite aucun détail, et la suite Jest couvre l'unitaire comme les routes — relancée à chaque modification pour la non-régression. »
À dire avant chaque mise en production ; montrez la suite verte.
Modèle copier-coller — validation Zod + erreurs + test Jest (code testé)
// 1) Validation (npm i zod) + 2) erreurs centralisées
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 requis'),
prix: z.number().positive('prix positif attendu')
});
const produits = [];
app.post('/api/v1/produits', (req, res, next) => {
try {
const donnees = schemaProduit.parse(req.body); // lève si invalide
const produit = { id: produits.length + 1, ...donnees };
produits.push(produit);
res.status(201).json(produit);
} catch (err) {
err.statut = 400; // attendue : entrée invalide
next(err);
}
});
// Middleware d'erreurs : TOUJOURS en dernier
// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
const statut = err.statut || 500;
const message = statut === 500 ? 'Erreur interne' : err.message;
res.status(statut).json({ erreur: message });
});
module.exports = app;
// 3) Test Jest (fichier app.test.js, npm i -D jest supertest) :
// const request = require('supertest');
// const app = require('./app');
// test('POST sans nom → 400', async () => {
// const res = await request(app).post('/api/v1/produits').send({ prix: 10 });
// expect(res.status).toBe(400);
// });
// test('POST valide → 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');
// });
Prototype d'apprentissage
Validation minimale + 2-3 tests sur le critique : formateur sans ralentir l'exploration.
API réelle en production
Comme Salim : schéma sur chaque entrée externe, middleware centralisé, suite Jest exigée verte avant merge.
Projet TypeScript
Zod valide ET type : un seul schéma pour la sécurité runtime et la robustesse compile-time.
Équipe qui découvre les tests
Commencer par les routes critiques (création, connexion) : valeur immédiate, habitude installée.
✓ Bonnes pratiques
- Valider avant de traiter, rejeter en 400 clair.
- Centraliser les erreurs en un middleware unique.
- Tester et relancer après chaque modification.
✗ Pièges fréquents
- Traiter req.body sans validation.
- Renvoyer la stack trace au client.
- Ne tester qu'à la main, une fois.
Points clés à retenir
- VALIDATION (Zod/Joi) : vérifier format/types/contraintes AVANT traitement, 400 clair si invalide. Ne jamais faire confiance au client.
- ERREURS centralisées : middleware (err, req, res, next) en dernier → réponse cohérente ; gérer l'ASYNC (try/catch).
- TESTS JEST : unitaires + intégration (route). expect(resultat).toBe(attendu).
- Bénéfices : CONFIANCE, NON-RÉGRESSION, documentation, meilleure conception. Le cap prototype → professionnel.
Défi 48h — appliquez demain
Ajoutez un schéma de validation à votre route la plus critique, le middleware d'erreurs, et 2 tests Jest (1 rejet 400, 1 succès) — suite verte exigée.
- Je peux expliquer validation, middleware d'erreurs, Jest.
- Je sais écrire un 400 clair et un 500 sans fuite.
- J'ai 2 tests verts sur ma route critique.
Questions fréquentes
Pourquoi tester quand « ça marche déjà » ?
Parce que « ça marche » ne vaut que pour les cas vérifiés à la main, aujourd'hui. Demain, chaque modification risque de casser silencieusement autre chose (régression) — et sans tests, il faudrait tout re-vérifier à la main à chaque fois, ce qu'on ne fait jamais. Les tests apportent confiance (on sait, on ne croit pas), non-régression (alerte immédiate après chaque changement), documentation du comportement attendu, et poussent à mieux structurer le code testable.
Validation, erreurs, tests : faut-il tout, pour tous les projets ?
Dosez selon l'enjeu. Prototype jetable : allégez pour aller vite, mais pratiquez un minimum (bonnes habitudes). Application réelle (utilisateurs, production, équipe) : indispensable — validation (données corrompues, bugs, failles), erreurs (sinon le serveur plante ou fuit des détails), tests (sinon les régressions passent jusqu'en production). Ces trois pratiques transforment un prototype en application fiable.
Où placer le middleware de gestion d'erreurs Express ?