Tests natifs de Node.js : premiers tests
Node.js intègre un lanceur de tests : le module node:test et la commande node --test. Aucune installation n’est nécessaire, ce qui convient aux scripts, aux bibliothèques et aux petits services (par exemple une API Express). Les exemples utilisent des noms génériques.
Quand choisir le lanceur natif
Section intitulée « Quand choisir le lanceur natif »| Besoin | Outil |
|---|---|
| Projet Node.js simple, zéro dépendance | node --test (cette rubrique) |
| Projet Vite, React, TypeScript, mocks de modules, interface | Vitest |
| Composants et DOM | Vitest |
Le lanceur natif est stable depuis Node.js 20 et complet depuis les versions LTS actuelles (voir Node.js : installation) : les fonctions décrites ici supposent une version LTS récente.
Un premier test
Section intitulée « Un premier test »src/somme.js :
export function somme(...nombres) { return nombres.reduce((total, n) => total + n, 0);}src/somme.test.js :
import { test } from 'node:test';import assert from 'node:assert/strict';import { somme } from './somme.js';
test('additionne plusieurs nombres', () => { assert.equal(somme(1, 2, 3), 6);});
test('renvoie 0 sans argument', () => { assert.equal(somme(), 0);});node --test✔ additionne plusieurs nombres (0.6ms)✔ renvoie 0 sans argument (0.1ms)ℹ tests 2ℹ pass 2ℹ fail 0node:assert/strict active les comparaisons strictes (assert.equal utilise ===) : à préférer à node:assert, dont equal est laxiste.
Quels fichiers sont exécutés
Section intitulée « Quels fichiers sont exécutés »node --test sans argument parcourt le dossier courant et exécute :
| Motif |
|---|
Fichiers dans un dossier nommé test |
*.test.js, *.test.mjs, *.test.cjs |
*-test.js, *_test.js |
test-*.js, test.js |
On peut aussi désigner explicitement des fichiers ou des motifs :
node --test src/somme.test.jsnode --test "src/**/*.test.js"node --test tests/Entourer le motif de guillemets pour que Node.js (et non le shell) l’interprète de la même façon sur toutes les machines.
describe, it et hooks
Section intitulée « describe, it et hooks »import { describe, it, before, after, beforeEach, afterEach } from 'node:test';import assert from 'node:assert/strict';
describe('Panier', () => { let panier;
before(() => { /* une fois avant tous les tests du groupe */ }); after(() => { /* une fois après */ }); beforeEach(() => { panier = new Panier(); }); afterEach(() => { panier.vider(); });
it('démarre vide', () => { assert.equal(panier.total(), 0); });
it('ajoute des articles', () => { panier.ajouter({ nom: 'Clavier', prixCentimes: 4900 }); assert.equal(panier.total(), 4900); });});describe et it sont des alias de la forme test : les deux styles se mélangent. Chaque test prépare son état et ne dépend pas de l’ordre d’exécution.
Options d’un test
Section intitulée « Options d’un test »test('opération lente', { timeout: 10_000 }, async () => {});test('désactivé', { skip: true }, () => {});test('désactivé pour une raison', { skip: 'bogue connu #12' }, () => {});test('à faire', { todo: 'gérer les remises' }, () => {});test('Linux seulement', { skip: process.platform !== 'linux' }, () => {});| Option | Effet |
|---|---|
skip |
Ignore le test (avec ou sans raison) |
todo |
Marque le test comme à faire (n’échoue pas la commande) |
timeout |
Délai maximal en millisecondes |
only |
Avec --test-only, exécute seulement ces tests |
concurrency |
Nombre de sous-tests simultanés |
Mode watch
Section intitulée « Mode watch »node --test --watchRelance les tests quand un fichier change. Pour cibler un nom de test :
node --test --test-name-pattern="ajoute"node --test --test-skip-pattern="lent"Scripts package.json
Section intitulée « Scripts package.json »{ "scripts": { "test": "node --test", "test:watch": "node --test --watch", "test:coverage": "node --test --experimental-test-coverage" }}TypeScript
Section intitulée « TypeScript »Les versions récentes de Node.js exécutent directement les fichiers TypeScript « effaçables » (types supprimés, sans compilation) :
node --test "src/**/*.test.ts"Limites : seules les annotations de types sont prises en charge (pas d’enum, de namespace, ni de syntaxes qui génèrent du code, sauf option dédiée), les imports relatifs doivent porter l’extension (./somme.ts), et les types ne sont pas vérifiés (lancer tsc --noEmit à part). Selon la version de Node.js, un drapeau peut être nécessaire : vérifier avec node --version et la documentation de la version utilisée. Pour des projets TypeScript plus riches, Vitest reste plus simple.
Premier test d’un service HTTP
Section intitulée « Premier test d’un service HTTP »import { test } from 'node:test';import assert from 'node:assert/strict';import { app } from '../src/app.js';
test('GET /sante répond ok', async (t) => { const serveur = app.listen(0, '127.0.0.1'); t.after(() => serveur.close()); const { port } = serveur.address();
const reponse = await fetch(`http://127.0.0.1:${port}/sante`); assert.equal(reponse.status, 200); assert.deepEqual(await reponse.json(), { statut: 'ok' });});Le port 0 laisse le système choisir un port libre ; t.after ferme le serveur à la fin du test (voir Express : API JSON).
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
Cannot use import statement outside a module |
"type": "module" absent, ou extension .mjs |
| Aucun test exécuté | Nom de fichier hors des motifs, mauvais dossier |
ERR_MODULE_NOT_FOUND |
Extension manquante dans un import relatif (./somme.js) |
| Le processus ne se termine pas | Serveur, connexion ou minuteur resté ouvert : fermer dans after |
assert.equal accepte 1 et '1' |
Importer node:assert/strict |
| Un test échoue sans message clair | assert.deepEqual affiche le diff ; ajouter un message en dernier argument |
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. Le paquet nodejs de la distribution peut être trop ancien pour certaines options : installer une version LTS récente (voir Node.js : installation).
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Documentation : Node.js, test runner
- Suite : Assertions