10

Tests Unitaires

Vitest, describe/it/expect, mocks, coverage

← Accueil
Pourquoi tester ?

1 Outil : Vitest

Vitest = le testeur moderne pour les projets Vite/Node. Compatible Jest (même API).

npm create vite@latest mon-projet -- --template vanilla
cd mon-projet
npm install -D vitest

Dans package.json :

{
  "scripts": {
    "test":     "vitest",
    "test:ui":  "vitest --ui",
    "coverage": "vitest run --coverage"
  }
}

2 Anatomie d'un test

// math.test.js
import { describe, it, expect } from 'vitest';
import { additionner, diviser } from './math.js';

describe('additionner', () => {
  it('additionne deux nombres positifs', () => {
    expect(additionner(2, 3)).toBe(5);
  });

  it('additionne avec un nombre négatif', () => {
    expect(additionner(-1, 3)).toBe(2);
  });
});

describe('diviser', () => {
  it('divise correctement', () => {
    expect(diviser(10, 2)).toBe(5);
  });

  it('lance une erreur si diviseur = 0', () => {
    expect(() => diviser(10, 0)).toThrow('Division par zéro');
  });
});

3 Les matchers essentiels

// Égalité
expect(2 + 2).toBe(4);                      // ===
expect({ a: 1 }).toEqual({ a: 1 });         // deep equal (objets)
expect(val).not.toBe(null);                  // négation

// Types / présence
expect(val).toBeTruthy();
expect(val).toBeFalsy();
expect(val).toBeNull();
expect(val).toBeUndefined();
expect(val).toBeDefined();

// Nombres
expect(0.1 + 0.2).toBeCloseTo(0.3);        // pour les flottants
expect(5).toBeGreaterThan(3);
expect(5).toBeLessThanOrEqual(5);

// Strings
expect("bonjour").toContain("jour");
expect("hello").toMatch(/^h/);

// Tableaux
expect([1, 2, 3]).toContain(2);
expect([1, 2, 3]).toHaveLength(3);

// Fonctions / erreurs
expect(() => fn()).toThrow();
expect(() => fn()).toThrow('message exact');
expect(() => fn()).toThrow(TypeError);

// Async
await expect(fetchData()).resolves.toEqual({ ok: true });
await expect(fetchBad()).rejects.toThrow('404');

4 Tests asynchrones

import { it, expect, vi } from 'vitest';

// Option 1 : async/await
it('fetche les données', async () => {
  const data = await fetchUsers();
  expect(data).toHaveLength(10);
  expect(data[0]).toHaveProperty('name');
});

// Option 2 : mock d'une API (ne pas faire de vrais appels en test)
it('affiche une erreur si fetch échoue', async () => {
  vi.spyOn(global, 'fetch').mockRejectedValueOnce(new Error('Network error'));
  await expect(fetchUsers()).rejects.toThrow('Network error');
  vi.restoreAllMocks();
});

5 Mocks & Spies

import { vi } from 'vitest';

// Mock d'une fonction
const fn = vi.fn();
fn("arg1");
expect(fn).toHaveBeenCalledWith("arg1");
expect(fn).toHaveBeenCalledTimes(1);

// Mock d'un module
vi.mock('./utils.js', () => ({
  calculerTVA: vi.fn(() => 100),
}));

// Spy : surveille une vraie fonction sans la remplacer
const spy = vi.spyOn(console, 'log');
maFonction(); // appelle console.log à l'intérieur
expect(spy).toHaveBeenCalled();
spy.mockRestore();

6 Couverture de code (coverage)

npm run coverage

Génère un rapport indiquant quel % de ton code est couvert :

💡 En production : viser 70-80% minimum. 100% n'est pas toujours rentable.

Les 5 règles des bons tests

  1. Un test = une chose — teste un seul comportement par it()
  2. ARRANGE / ACT / ASSERT — prépare les données, exécute, vérifie
  3. Noms clairs"diviser retourne une erreur si diviseur = 0" pas "test 3"
  4. Indépendants — l'ordre des tests ne doit pas compter
  5. Rapides — pas de vrais appels réseau (utilise des mocks)
🧪

Mini-Projet : Tests Unitaires

Un fichier de tests à lancer avec npm test dans un projet Vitest configuré.

Exercices & Solutions

Télécharge les fichiers, ouvre-les dans ton éditeur et travaille directement dedans.

📝 exercices.js

5 exercices : tester calculerTVA, validerEmail, grouperPar, paginer et une classe complète.

🟢 ×2 Facile 🟡 ×2 Moyen 🔴 ×1 Difficile
⬇ Télécharger
solutions.js

Corrections complètes. À consulter après avoir essayé.

Corrigé complet Best practices
⬇ Télécharger
🧠 Tester mes connaissances