Aller au contenu

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.

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.

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);
});
Fenêtre de terminal
node --test
✔ additionne plusieurs nombres (0.6ms)
✔ renvoie 0 sans argument (0.1ms)
ℹ tests 2
ℹ pass 2
ℹ fail 0

node:assert/strict active les comparaisons strictes (assert.equal utilise ===) : à préférer à node:assert, dont equal est laxiste.

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 :

Fenêtre de terminal
node --test src/somme.test.js
node --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.

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.

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
Fenêtre de terminal
node --test --watch

Relance les tests quand un fichier change. Pour cibler un nom de test :

Fenêtre de terminal
node --test --test-name-pattern="ajoute"
node --test --test-skip-pattern="lent"
{
"scripts": {
"test": "node --test",
"test:watch": "node --test --watch",
"test:coverage": "node --test --experimental-test-coverage"
}
}

Les versions récentes de Node.js exécutent directement les fichiers TypeScript « effaçables » (types supprimés, sans compilation) :

Fenêtre de terminal
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.

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).

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).