Vitest : écrire des tests
Structure
Section intitulée « Structure »import { afterEach, beforeEach, describe, expect, it } from 'vitest';
describe('Panier', () => { let panier: Panier;
beforeEach(() => { panier = new Panier(); });
afterEach(() => { panier.vider(); });
it('démarre vide', () => { expect(panier.total()).toBe(0); });
it('ajoute des articles', () => { panier.ajouter({ nom: 'Clavier', prixCentimes: 4900 }); expect(panier.total()).toBe(4900); });});| Fonction | Rôle |
|---|---|
describe(nom, fn) |
Regroupe des tests |
it(nom, fn) / test(nom, fn) |
Un test (synonymes) |
beforeAll, afterAll |
Une fois pour le groupe |
beforeEach, afterEach |
Avant et après chaque test |
Écrire des tests indépendants : chacun prépare son état (beforeEach) et ne dépend pas de l’ordre d’exécution.
Assertions : expect
Section intitulée « Assertions : expect »expect(2 + 2).toBe(4); // égalité stricte (Object.is)expect({ a: 1 }).toEqual({ a: 1 }); // égalité de valeur (objets, tableaux)expect({ a: 1, b: undefined }).toStrictEqual({ a: 1, b: undefined }); // plus strictexpect([1, 2, 3]).toContain(2);expect('bonjour monde').toMatch(/monde/);expect(0.1 + 0.2).toBeCloseTo(0.3); // nombres décimauxexpect(valeur).toBeTruthy();expect(valeur).toBeNull();expect(valeur).toBeDefined();expect(liste).toHaveLength(3);expect(objet).toHaveProperty('nom', 'Alice');expect(objet).toMatchObject({ nom: 'Alice' }); // sous-ensemble de propriétésexpect(nombre).toBeGreaterThan(3);expect(() => fonction()).toThrow('message');expect(fonction).not.toThrow();| Matcher | Quand |
|---|---|
toBe |
Primitives, identité d’objet |
toEqual |
Comparer le contenu d’objets et tableaux |
toStrictEqual |
Idem, en distinguant undefined et les classes |
toMatchObject |
Vérifier seulement certains champs |
toBeCloseTo |
Nombres à virgule |
toThrow |
Erreur attendue (envelopper l’appel dans une fonction) |
expect(a).toBe(b) sur deux objets distincts échoue même s’ils ont le même contenu : utiliser toEqual.
Tests asynchrones
Section intitulée « Tests asynchrones »it('charge un client', async () => { const client = await chargerClient(1); expect(client.nom).toBe('Alice');});
it('rejette si le client est inconnu', async () => { await expect(chargerClient(999)).rejects.toThrow('introuvable');});
it('résout avec un tableau', async () => { await expect(listerClients()).resolves.toHaveLength(2);});Toujours attendre (await) les assertions sur des promesses : sans await, le test peut passer alors que l’assertion échoue après sa fin.
Tester les erreurs
Section intitulée « Tester les erreurs »function diviser(a: number, b: number) { if (b === 0) throw new RangeError('division par zéro'); return a / b;}
it('refuse la division par zéro', () => { expect(() => diviser(1, 0)).toThrow(RangeError); expect(() => diviser(1, 0)).toThrow('division par zéro');});Plusieurs cas : test.each
Section intitulée « Plusieurs cas : test.each »import { describe, expect, it } from 'vitest';
describe('estEmailValide', () => { it.each([ ['alice@exemple.com', true], ['alice@', false], ['@exemple.com', false], ['', false], ])('estEmailValide(%j) vaut %s', (entree, attendu) => { expect(estEmailValide(entree)).toBe(attendu); });});Avec des objets nommés :
it.each([ { a: 1, b: 2, attendu: 3 }, { a: -1, b: 1, attendu: 0 },])('somme($a, $b) = $attendu', ({ a, b, attendu }) => { expect(somme(a, b)).toBe(attendu);});Les formats %s (chaîne), %d (nombre), %j (JSON) et $champ construisent le nom de chaque cas.
Cibler, ignorer, planifier
Section intitulée « Cibler, ignorer, planifier »it.only('seul ce test est exécuté', () => {}); // à ne jamais committerit.skip('désactivé temporairement', () => {});it.todo('à écrire : gérer les remises');it.skipIf(process.platform === 'win32')('Linux seulement', () => {});it.runIf(process.env.CI)('uniquement en intégration continue', () => {});it.fails('échoue volontairement (bogue connu)', () => { expect(1).toBe(2);});only laissé dans le code désactive silencieusement tous les autres tests : l’option allowOnly: false en CI le refuse (voir Configuration et intégration continue).
Depuis la ligne de commande :
npx vitest run -t "ajoute des articles" # par nom de testnpx vitest run src/panier # par cheminit('opération lente', async () => { await operationLente();}, 10_000); // délai maximal en ms (5 s par défaut)Dans la configuration : testTimeout et hookTimeout.
Bonnes pratiques
Section intitulée « Bonnes pratiques »- Un test vérifie un comportement, nommé comme une phrase (
it('refuse un email invalide')). - Structure organiser, agir, vérifier (arrange, act, assert).
- Tester le comportement observable, pas les détails d’implémentation.
- Pas d’accès au réseau ni au disque dans les tests unitaires (voir Mocks, espions et temps).
- Données de test déterministes : pas d’aléatoire ni de date du jour sans contrôle du temps.
- Un test qui échoue doit dire pourquoi : préférer
toEqualavec les valeurs attendues àtoBeTruthy.
Tester une API Express
Section intitulée « Tester une API Express »npm install -D supertest @types/supertestimport request from 'supertest';import { describe, expect, it } from 'vitest';import { app } from '../src/app';
describe('GET /sante', () => { it('répond ok', async () => { const reponse = await request(app).get('/sante'); expect(reponse.status).toBe(200); expect(reponse.body).toEqual({ statut: 'ok' }); });});supertest démarre l’application sur un port éphémère (voir Express : API JSON pour séparer app et le démarrage).
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
| Un test passe alors qu’il devrait échouer | Promesse non attendue (await manquant), assertion jamais atteinte |
toBe échoue sur deux objets identiques |
Utiliser toEqual |
Test timed out in 5000ms |
Opération non terminée : promesse jamais résolue, minuteur simulé non avancé |
| Tests dépendants de l’ordre | État partagé entre tests : beforeEach pour réinitialiser |
expected … to throw sans erreur |
Appel non enveloppé dans une fonction |
Nom de test illisible avec each |
Formats %s, %j, $champ |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Aucune.