Qu’est-ce que Vitest ?
Vitest est un test runner propulsé par Vite. Il réutilise votre configuration Vite existante — alias, plugins, transformations TypeScript et JSX — afin que vos tests passent par le même pipeline que votre application. Cette base commune est ce qui lui permet de démarrer rapidement et de rester performant.
L’API vous semblera familière si vous avez déjà utilisé Jest. describe, it, expect et les outils de mocking fonctionnent de la même manière. Ce qui change, c’est le moteur sous-jacent : Vitest exécute les tests dans des workers en parallèle, surveille les fichiers via le graphe de modules de Vite et offre un support TypeScript natif sans configuration supplémentaire.
Écrire votre premier test
Un fichier de test importe des éléments depuis vitest et décrit le comportement qu’il vérifie.
// sum.test.ts
import { describe, it, expect } from "vitest";
import { sum } from "./sum";
describe("sum", () => {
it("adds two numbers", () => {
expect(sum(2, 3)).toBe(5);
});
it("handles negatives", () => {
expect(sum(-1, -1)).toBe(-2);
});
});
Nommez vos tests en fonction du comportement et non de la fonction. Un test en échec intitulé « ajoute deux nombres » vous indique précisément ce qui est cassé ; un test intitulé « test somme 1 » ne vous apporte aucune information. Groupez les cas liés avec describe et utilisez beforeEach et afterEach pour la configuration et le nettoyage.
Assertions
Vitest propose une API de matchers complète, très proche de celle de Jest.
// assertions.test.ts
expect(value).toBe(5); // strict equality
expect(value).toEqual({ a: 1 }); // deep equality
expect(value).toBeTruthy();
expect(list).toHaveLength(3);
expect(list).toContain("a");
expect(fn).toThrow("invalid");
expect(value).toBeGreaterThan(3);
Utilisez toEqual pour les objets et les tableaux, et toBe pour les primitives et les vérifications de référence. toMatchObject et toContainEqual sont utiles lorsque vous ne vous intéressez qu’à une partie d’une structure.
Mocks et spies
L’isolation est au cœur des tests unitaires. Vitest propose vi.fn pour les mocks, vi.spyOn pour encapsuler des méthodes existantes et vi.mock pour remplacer des modules entiers.
// api.test.ts
import { vi, describe, it, expect } from "vitest";
import { fetchUser } from "./api";
vi.mock("./http", () => ({
get: vi.fn().mockResolvedValue({ id: 1, name: "Ada" }),
}));
describe("fetchUser", () => {
it("returns the user", async () => {
await expect(fetchUser(1)).resolves.toEqual({ id: 1, name: "Ada" });
});
});
vi.mock est “hoisted” (remonté) au-dessus des imports, ainsi le mock est enregistré avant que le module testé ne le charge. Lorsque la factory a besoin d’une variable définie dans le fichier de test, utilisez vi.hoisted pour que la valeur existe avant l’exécution du mock.
Tests asynchrones
Attendez la fin de l’opération et effectuez des assertions sur le résultat. Vitest fournit les helpers resolves et rejects pour les promesses.
// async.test.ts
it("rejects on failure", async () => {
await expect(loadUser(-1)).rejects.toThrow("Invalid id");
});
Privilégiez ce style au callback done. Oublier done provoque des timeouts difficiles à diagnostiquer, et les rejets non gérés peuvent s’infiltrer d’un test à l’autre. Pour les timers factices, vi.useFakeTimers() et vi.advanceTimersByTime() vous permettent de tester les debounces et les intervalles de manière déterministe.
Snapshots
Les snapshots capturent une valeur pour la comparer lors des exécutions ultérieures. Ils sont utiles pour les sorties volumineuses et stables, comme des données sérialisées ou du markup rendu, mais ils sont faciles à utiliser à mauvais escient.
// snapshot.test.ts
it("matches the shape", () => {
expect(createConfig({ debug: true })).toMatchSnapshot();
});
Un snapshot mis à jour sans être relu est pire qu’une absence de test. Utilisez-les pour des structures de données où une comparaison complète est impraticable, et privilégiez les assertions explicites lorsque vous savez précisément ce qui importe. toMatchInlineSnapshot conserve la valeur attendue dans le fichier de test, ce qui rend les revues de code pertinentes.
Configuration
Vitest lit un bloc test depuis votre configuration Vite ou un vitest.config.ts dédié.
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "jsdom",
globals: true,
coverage: {
provider: "v8",
reporter: ["text", "html"],
thresholds: { lines: 80 },
},
},
});
L’option environment permet de choisir entre node, jsdom, happy-dom ou un navigateur. Pour les tests de composants, jsdom ou happy-dom sont généralement utilisés ; pour la logique pure, conservez l’environnement node qui est plus rapide. Les seuils de couverture (coverage thresholds) permettent de transformer un objectif de couverture en une règle contraignante.
Vitest et la pyramide des tests
Vitest constitue la couche des tests unitaires et d’intégration. Associez-le à une bibliothèque de tests de composants telle que Testing Library pour le comportement de l’UI, et à un outil de bout en bout comme Playwright pour les flux utilisateurs complets. Ensemble, ils couvrent la pyramide : de nombreux tests unitaires rapides, moins de tests d’intégration et une poignée de tests de bout en bout.
Bonnes pratiques
- Testez le comportement, pas les détails d’implémentation.
- Chaque test doit se concentrer sur un seul comportement.
- Utilisez
beforeEachpour la configuration partagée et nettoyez vos mocks avecvi.clearAllMocks. - Privilégiez
resolvesetrejectsaux callbacksdone. - Mockez aux frontières (réseau, temps, aléatoire), et non chaque appel interne.
- Utilisez des seuils de couverture (coverage thresholds) pour éviter les régressions silencieuses.
- Exécutez vos tests en mode watch pendant le développement et en CI à chaque push.
Erreurs courantes
- Effectuer des assertions sur l’état interne ou des méthodes privées plutôt que sur le comportement observable.
- Mettre à jour les snapshots sans examiner le diff.
- Oublier de réinitialiser les mocks entre les tests, entraînant ainsi des fuites d’état.
- Utiliser des timers réels, ce qui rend les tests lents et instables (flaky).
- Trop mocker au point que le test ne vérifie plus que les mocks eux-mêmes.
- Tester le même comportement à chaque niveau de la pyramide.
Et après ?
Vitest est le moyen le plus rapide de commencer à tester un projet Vite. Comparez-le avec le classique Jest, ajoutez Testing Library pour vos composants et Playwright pour vos flux de bout en bout, et appuyez-vous sur Vite pour maintenir une configuration partagée. Ensuite, écrivez quelques tests pour votre code existant et voyez vos refactorisations devenir sécurisées.