Tests natifs de Node.js : organisation et intégration continue
Arborescence
Section intitulée « Arborescence »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.
Séparer les scripts
Section intitulée « Séparer les scripts »{ "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.
Données et utilitaires de test
Section intitulée « Données et utilitaires de test »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).
Base de données de test
Section intitulée « Base de données de test »- 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.
process.env.NODE_ENV = 'test';process.env.DATABASE_URL ??= 'sqlite::memory:';process.env.TZ = 'UTC';Hooks Git
Section intitulée « Hooks Git »Lancer les tests avant de pousser, et l’analyse avant de committer (voir Husky : installation et Husky : hooks et lint-staged) :
npm testGarder le pre-commit rapide (formatage, analyse des fichiers modifiés : ESLint, Prettier).
Intégration continue
Section intitulée « Intégration continue ».gitea/workflows/ci.yaml :
name: CIon: 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:toutLa 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.
Tests et qualité du code : l’ensemble
Section intitulée « Tests et qualité du code : l’ensemble »| É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 |
Bonnes pratiques
Section intitulée « Bonnes pratiques »- 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).
Dépannage
Section intitulée « Dépannage »| 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).