Aller au contenu principal

Tests et observabilité

Progression

#Tests et observabilité

Des tests unitaires pour la logique pure, d’intégration pour les endpoints critiques et de contract pour stabiliser l’API côté client. Ajoutez des logs structurés (JSON), des métriques (latences, taux d’erreur) et de la traçabilité (correlation id) pour diagnostiquer. Les tests ne remplacent pas l’observabilité; l’observabilité ne remplace pas les tests.

Les tests d’intégration démarrent l’app avec des dépendances éphémères (DB en mémoire/conteneur) et valident des parcours réalistes. Les mocks s’utilisent avec mesure: moquez le réseau lointain, pas la base si votre code dépend des index et transactions. Les tests de charge légers capturent régressions flagrantes (latences x10, fuites mémoire) avant la prod.

Mini‑exercice: testez POST /signup (chemin heureux, email déjà pris, mot de passe trop court) puis ajoutez un test de contrat qui vérifie la structure de l’erreur (Problem Details) et la présence d’un traceId.

#Animation: pyramide de tests pragmatique

Unitaires
Rapides, isolés, logique pure
Intégration
Infra éphémère; endpoints critiques
Contrats
Stabiliser l’API (client/serveur)
E2E
Parcours clés; flakiness maîtrisée
Perf
Smoke load; budgets en CI

#Diagramme: traçage d’une requête (logs, métriques, traces)

Client
API
DB
Observabilité
1. POST /signup
2. INSERT users (tx)
3. OK
4. 201 Created
5. Log JSON + métriques (latence, status) + traceId

#Anti‑flakiness checklist

  • Données et seeds déterministes; horloges figées (fake timers) pour éviter les effets du temps.
  • Retrys uniquement sur opérations idempotentes; backoff borné; limites claires.
  • Infra éphémère pour l’intégration (DB conteneur/mémoire); pas d’appels réseau externes réels.
  • Timeouts explicites par test; journaux/artefacts collectés automatiquement en cas d’échec.
  • Tests de contrat pour stabiliser l’interface et éviter les cassures silencieuses côté client.

#Exemples de code pratiques

#Intégration: POST /signup avec Supertest

tsts
1import request from 'supertest'2import { app } from '../src/app'3import { createTestDb, resetDb, destroyDb } from './helpers/db'4 5beforeAll(async () => { await createTestDb() })6afterAll(async () => { await destroyDb() })7beforeEach(async () => { await resetDb() })8 9describe('POST /signup', () => {10  it('chemin heureux', async () => {11    const res = await request(app)12      .post('/signup')13      .send({ email: 'a@b.c', password: 'S3cure#123' })14      .set('x-correlation-id', 't-1')
Dépendances éphémères

Démarrez une base jetable par suite de tests (containers, mémoire) et appliquez les migrations au setup. Nettoyez entre tests (TRUNCATE/transactions) pour l’isolation.

#Tests de contrat: Problem Details + traceId

tsts
1import request from 'supertest'2import { z } from 'zod'3import { app } from '../src/app'4 5const Problem = z.object({6  type: z.string().url(),7  title: z.string(),8  status: z.number().int(),9  detail: z.string().optional(),10  traceId: z.string().optional(),11  errors: z.array(z.object({ field: z.string(), message: z.string() })).optional(),12})13 14test('Problem Details shape', async () => {

#Logs structurés + correlation id

tsts
1import pino from 'pino'2import { randomUUID } from 'node:crypto'3export const logger = pino({ level: process.env.LOG_LEVEL || 'info' })4 5export function withCorrelationId(req, _res, next) {6  req.id = req.get('x-correlation-id') || randomUUID()7  req.logger = logger.child({ traceId: req.id })8  next()9}10 11app.use(withCorrelationId)12 13app.post('/signup', async (req, res) => {14  const start = Date.now()

#Smoke de charge (10s)

bashbash
1npx autocannon -m POST -H 'content-type: application/json' -d 10 -c 20 -b '{"email":"a@b.c","password":"S3cure#123"}' http://localhost:3000/signup

#Quiz rapide

Quelle stratégie d’environnement privilégier pour des tests d’intégration fiables ?
Quelle stratégie d’environnement privilégier pour des tests d’intégration fiables ?
Quel signal doit figurer dans chaque log pour corréler une requête ?
Quel signal doit figurer dans chaque log pour corréler une requête ?