Aller au contenu

Tests natifs de Node.js : organisation et intégration continue

monprojet/
├── package.json
├── src/
│ ├── panier.js
│ ├── panier.test.js # test unitaire à côté du code
│ └── ...
└── tests/
├── setup.js # préparation globale (--import)
├── fixtures/ # données de test
├── integration/
│ └── api.test.js # tests avec serveur et base de données
└── aide/
└── serveur.js # utilitaires partagés
Type Portée Vitesse Où
Unitaire Une fonction ou classe, sans réseau ni disque Très rapide À côté du code
Intégration Plusieurs modules, base de données, serveur HTTP Plus lent tests/integration/
Bout en bout Application complète dans un navigateur Lent Outil dédié (Playwright)

Beaucoup de tests unitaires, un nombre raisonnable de tests d’intégration, quelques parcours de bout en bout.

{
"scripts": {
"test": "node --test src/",
"test:integration": "node --import ./tests/setup.js --test tests/integration/",
"test:tout": "node --import ./tests/setup.js --test",
"test:watch": "node --test --watch src/"
}
}

Le script par défaut (test) doit être rapide pour être lancé souvent.

tests/aide/serveur.js
import { app } from '../../src/app.js';
export async function demarrerServeur() {
const serveur = app.listen(0, '127.0.0.1');
await new Promise((resolve) => serveur.once('listening', resolve));
const base = `http://127.0.0.1:${serveur.address().port}`;
return {
base,
arreter: () => new Promise((resolve) => serveur.close(resolve)),
};
}
import { after, before, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { demarrerServeur } from '../aide/serveur.js';
describe('API clients', () => {
let serveur;
before(async () => {
serveur = await demarrerServeur();
});
after(() => serveur.arreter());
it('liste les clients', async () => {
const reponse = await fetch(`${serveur.base}/api/clients`);
assert.equal(reponse.status, 200);
});
});

Fixtures : des fichiers JSON dans tests/fixtures/, lus avec readFile et JSON.parse (voir Node.js : premiers pas).

  • Utiliser une base dédiée (jamais celle de production), configurée par variable d’environnement dans tests/setup.js.
  • SQLite en mémoire ou dans un dossier temporaire pour des tests rapides (voir SQLite : installation).
  • Une base réelle dans un conteneur jetable pour des tests proches de la production (voir Docker : conteneurs).
  • Réinitialiser les données avant chaque test (transaction annulée, tables vidées) pour qu’ils restent indépendants.
tests/setup.js
process.env.NODE_ENV = 'test';
process.env.DATABASE_URL ??= 'sqlite::memory:';
process.env.TZ = 'UTC';

Lancer les tests avant de pousser, et l’analyse avant de committer (voir Husky : installation et Husky : hooks et lint-staged) :

.husky/pre-push
npm test

Garder le pre-commit rapide (formatage, analyse des fichiers modifiés : ESLint, Prettier).

.gitea/workflows/ci.yaml :

name: CI
on:
push:
branches: [main]
pull_request:
jobs:
qualite:
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
node: [22, 24]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm run lint
- run: npm run format:check
- run: npm run test:tout

La matrice teste plusieurs versions de Node.js : utile pour une bibliothèque ; pour une application, tester la version utilisée en production. Voir Gitea Actions : déclencheurs, jobs et conditions.

Étape Outil Quand
Formatage Prettier À l’enregistrement, au pre-commit
Analyse du code ESLint Au pre-commit, en CI
Tests rapides node --test ou Vitest En continu, au pre-push
Tests complets et couverture Idem En CI
Hooks Husky Automatisation locale
  • Les tests échouent pour la bonne raison : un test qui n’a jamais échoué n’est pas fiable (le casser volontairement une fois).
  • Aucun test ne dépend du réseau externe, de la date du jour ou de l’ordre.
  • Nettoyer ce que le test crée (fichiers, ports, connexions).
  • Corriger ou supprimer un test instable plutôt que le relancer jusqu’au succès.
  • Lire le diff avant de mettre à jour un snapshot.
  • Lancer la suite exactement comme en CI avant de pousser (npm ci && npm run test:tout).
Symptôme Piste
Les tests passent en local, échouent en CI Variables d’environnement, fuseau horaire, version de Node.js, ordre, données partagées
EADDRINUSE en parallèle Port 0
Données qui fuient entre tests Réinitialiser dans beforeEach, base de test dédiée
Tests lents Séparer unitaires et intégration, ne démarrer la base qu’une fois (before), réduire la concurrence
CI très longue Cache npm (cache: npm), --test-concurrency, parallélisme par matrix
Le script test ne trouve rien Dossier ciblé inexact : node --test src/

Ubuntu 24.04 et 26.04 : différences à connaître

Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »

Aucune propre à Ubuntu. Sur le poste ou le serveur de CI sous Ubuntu, installer une version LTS de Node.js adaptée (voir Node.js : installation).