Introduction
Vitest est l'outil de test moderne par excellence en 2026, conçu spécifiquement pour Vite. Il offre des performances fulgurantes grâce au support natif des modules ESM, à la résolution rapide des dépendances et à une API familière inspirée de Jest. Contrairement aux outils traditionnels comme Jest qui peuvent être lents sur les gros projets, Vitest parallelise les tests par défaut et intègre parfaitement les environnements comme JSDOM pour les composants React ou Vue.
Pourquoi l'adopter ? Dans un monde où les CI/CD exigent des feedbacks rapides, Vitest réduit les temps de test de 50-80% par rapport à Jest. Il gère les snapshots, les mocks, la couverture de code et même un UI de test interactif. Ce tutoriel intermediate vous guide pas à pas pour créer un projet Vite + React + TypeScript, configurer Vitest, écrire des tests avancés (mocks, async, snapshots) et mesurer la couverture. À la fin, vous aurez un setup production-ready que vous bookmerez pour vos futurs projets. Prêt à tester comme un pro ? (128 mots)
Prérequis
- Node.js 20+ installé
- Connaissances de base en Vite, React et TypeScript
- Un éditeur comme VS Code avec extension Vitest
- Git pour versionner le projet
Créer le projet Vite de base
npm create vite@latest vitest-tutorial -- --template react-ts
cd vitest-tutorial
npm installCette commande initialise un projet Vite avec template React + TypeScript. Elle installe toutes les dépendances de base. Évitez les templates vanilla pour ce tutoriel, car React permet des tests DOM réalistes ; relancez npm install pour valider.
Installer Vitest et dépendances de test
npm install -D vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom @vitest/ui happy-dom happy-dom/test-environment
npm install -D @types/node @vitest/coverage-v8On ajoute Vitest comme devDependency, avec RTL pour tester React, JSDOM/HappyDOM pour simuler le navigateur, et coverage-v8 pour les rapports. @vitest/ui apporte une interface web interactive. Le piège : oublier les types Node pour les imports fs/path.
Configurer vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
export default defineConfig({
plugins: [react()], test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/test-setup.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: ['node_modules', 'dist', '**/*.config.*'],
},
},
resolve: {
alias: {
'@': resolve(__dirname, './src'),
},
},
});Ce fichier étend la config Vite pour les tests : globals active describe/it/expect sans imports, jsdom simule le DOM, coverage génère des rapports. Ajoutez un alias '@/' pour importer src facilement. Piège : sans plugins: [react()], les JSX ne compilent pas en test.
Créer le setup global des tests
import '@testing-library/jest-dom';Ce fichier s'exécute avant chaque test suite, étendant expect avec des matchers DOM comme toBeInTheDocument. Court et efficace, il évite les imports répétitifs. Piège : l'oublier casse les assertions RTL.
Ajouter les scripts de test dans package.json
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"test:unit": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage"
}
}On ajoute des scripts npm pour lancer les tests (vitest), l'UI (--ui), et la coverage. vitest run pour CI. Copiez juste la section scripts dans votre package.json existant. Piège : utiliser npm test sans le définir pointe sur Vitest.
Créer un composant à tester (compteur)
import { useState } from 'react';
type CounterProps = {
initialValue?: number;
};
export function Counter({ initialValue = 0 }: CounterProps) {
const [count, setCount] = useState(initialValue);
return (
<div>
<h2>Compteur: {count}</h2>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(initialValue)}>Reset</button>
</div>
);
}Ce composant simple utilise useState pour un compteur incrémentable. Props optionnelles pour flexibilité. Idéal pour tester renders, events et state. Piège : sans key sur boutons, React peut warn en tests batchés.
Écrire des tests unitaires basiques
import { describe, it, expect } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { Counter } from './Counter';
describe('Counter', () => {
it('affiche le compteur initial à 0', () => {
render(<Counter />);
expect(screen.getByText('Compteur: 0')).toBeInTheDocument();
});
it('incrémente le compteur', async () => {
render(<Counter />);
fireEvent.click(screen.getByText('+'));
expect(screen.getByText('Compteur: 1')).toBeInTheDocument();
});
it('réinitialise avec prop initialValue', () => {
render(<Counter initialValue={5} />);
fireEvent.click(screen.getByText('Reset'));
expect(screen.getByText('Compteur: 5')).toBeInTheDocument();
});
});Ces tests vérifient render initial, interactions userEvent et props. fireEvent simule clics. Avec globals: true, pas d'import describe/it. Piège : utiliser await sans act() pour useState updates ; ici async pour cohérence.
Tests avancés avec mocks et snapshots
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { Counter } from './Counter';
const mockConsole = vi.fn();
describe('Counter avancés', () => {
it('utilise un mock pour console.log', () => {
vi.spyOn(console, 'log').mockImplementation(mockConsole);
render(<Counter initialValue={10} />);
fireEvent.click(screen.getByText('-'));
expect(mockConsole).toHaveBeenCalledWith('Compteur décrémenté');
vi.restoreAllMocks();
});
it('snapshot du rendu initial', () => {
const { asFragment } = render(<Counter initialValue={42} />);
expect(asFragment()).toMatchSnapshot();
});
});On mocke console avec vi.spyOn (API Vitest native), teste appels, et snapshot pour valider JSX. vi.restoreAllMocks() nettoie. Ajoutez ceci après les tests basiques. Piège : snapshots changent avec props ; updatez avec vitest -u.
Lancer et déboguer les tests
Exécutez npm run test:unit pour watch mode (tests relancent auto). npm run test:ui ouvre http://localhost:51204 pour filtrer/focus tests. Pour coverage : npm run test:coverage génère coverage/coverage-final.json et index.html. Utilisez --reporter=verbose pour traces détaillées.
Bonnes pratiques
- Utilisez
vi.mockpour modules externes : Mockez fetch/API avant imports avec chemin dynamique (vi.mock('./api')). - Activez coverage thresholds dans vitest.config.ts :
{ lines: 80, functions: 80 }pour CI fails. - Préférez
userEventàfireEventpour events réalistes (type, hover). - Tests isolés : Un
itpar comportement, max 10 assertions. - Workspace multi-projets : Vitest supporte
projects: ['./packages/*']pour monorepos.
Erreurs courantes à éviter
- JSDOM non configuré : Erreurs 'document is undefined' → ajoutez
environment: 'jsdom'. - Imports manquants en non-globals : Si globals:false, importez
describe/it/expectexplicitement. - Mocks non restaurés : Polue tests suivants → toujours
vi.restoreAllMocks()ouafterEach. - Coverage inexacte : Excluez node_modules/dist explicitement, utilisez
provider: 'v8'pour ESM.
Pour aller plus loin
- Docs officielles : Vitest
- Guide RTL : Testing Library
- Formations Learni sur les tests avancés : Maîtrisez Vitest en enterprise avec CI/CD et E2E.
- Projet GitHub exemple : Forkez ce tuto pour expérimenter !