Outils & Frameworks

Vitest : l'Alternative Moderne à Jest pour vos Projets

Romain Lefebvre

Romain Lefebvre

4 avril 2026

Vitest : l'Alternative Moderne à Jest pour vos Projets

Pourquoi envisager Vitest ?

Si vous travaillez avec Vite — et si vous faites du Vue, du React moderne, ou du Svelte, c'est très probablement votre cas — alors Vitest est la suite logique de votre stack.

La promesse de Vitest est simple : utiliser la même configuration et la même infrastructure que Vite pour vos tests. Résultat : pas de transpilation séparée, ESM natif, TypeScript out-of-the-box, et des temps de démarrage qui donnent envie de relancer ses tests en boucle.

Quelques chiffres concrets sur un projet React typique :

  • Jest (avec Babel) : démarrage ~4-6s, tests parallèles via workers
  • Vitest : démarrage ~0.5-1s, workers basés sur Vite HMR

La différence se ressent encore plus en mode watch, où Vitest ne retransform que les fichiers modifiés.

Installation et configuration initiale

# Dans un projet Vite existant
npm install --save-dev vitest

# Avec les utilitaires recommandés
npm install --save-dev @vitest/ui @vitest/coverage-v8

Configuration dans vite.config.ts (unified config — un seul fichier pour tout) :

/// <reference types="vitest" />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    // Environnement DOM pour les tests React/Vue
    environment: 'jsdom',
    
    // Simule un globals Jest (describe, it, expect sans import)
    globals: true,
    
    // Setup file pour les configurations globales
    setupFiles: ['./src/test/setup.ts'],
    
    // Inclure uniquement certains fichiers
    include: ['src/**/*.{test,spec}.{js,ts,jsx,tsx}'],
    
    // Couverture
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html', 'lcov'],
      exclude: [
        'node_modules/',
        'src/test/',
        '**/*.d.ts',
        '**/*.config.*',
      ],
      thresholds: {
        lines: 80,
        functions: 80,
        branches: 75,
        statements: 80,
      },
    },
  },
});

Pour les projets sans Vite (Node.js pur), utilisez un fichier dédié :

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'node',
    globals: true,
    include: ['src/**/*.test.ts'],
    coverage: {
      provider: 'v8',
    },
  },
});

Migrer depuis Jest

La compatibilité API de Vitest est très haute — la plupart des suites Jest migrent sans toucher les tests eux-mêmes. Voici les différences à connaître :

1. Les imports explicites (ou le mode globals)

// Jest — pas d'import nécessaire
it('teste quelque chose', () => {
  expect(42).toBe(42);
});

// Vitest — deux options

// Option A : imports explicites (recommandé)
import { describe, it, expect, vi } from 'vitest';

it('teste quelque chose', () => {
  expect(42).toBe(42);
});

// Option B : globals: true dans la config (compatibilité Jest maximale)
// Aucun import nécessaire, comme Jest

2. jest devient vi

// Jest
jest.fn()
jest.mock('./module')
jest.spyOn(obj, 'method')
jest.useFakeTimers()
jest.clearAllMocks()

// Vitest — même API, préfixe différent
vi.fn()
vi.mock('./module')
vi.spyOn(obj, 'method')
vi.useFakeTimers()
vi.clearAllMocks()

3. Les mocks de modules

// Les deux syntaxes fonctionnent pareil
vi.mock('./my-module', () => ({
  myFunction: vi.fn().mockReturnValue('mocked'),
  MyClass: vi.fn().mockImplementation(() => ({
    method: vi.fn(),
  })),
}));

// Import avec types mockés
import { myFunction } from './my-module';
const mockMyFunction = vi.mocked(myFunction);
mockMyFunction.mockResolvedValue({ data: 'test' });

4. Les snapshot tests

// Identique à Jest
it('correspond au snapshot', () => {
  const component = render(<Button label="Cliquer" />);
  expect(component).toMatchSnapshot();
});

// Inline snapshot (très pratique)
it('formate correctement la date', () => {
  const result = formatDate(new Date('2024-01-15'));
  expect(result).toMatchInlineSnapshot(`"15 janvier 2024"`);
});

Fonctionnalités exclusives à Vitest

Interface utilisateur intégrée

npx vitest --ui

L'interface web de Vitest (sur localhost:51204) permet de naviguer dans les tests, voir les erreurs avec coloration syntaxique, et relancer des tests individuels. Aucune configuration nécessaire.

Test en parallèle dans le même worker

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

describe.concurrent('requêtes API parallèles', () => {
  it.concurrent('endpoint 1 répond en moins de 200ms', async () => {
    const start = Date.now();
    await fetch('https://api.example.com/endpoint1');
    expect(Date.now() - start).toBeLessThan(200);
  });

  it.concurrent('endpoint 2 répond en moins de 200ms', async () => {
    const start = Date.now();
    await fetch('https://api.example.com/endpoint2');
    expect(Date.now() - start).toBeLessThan(200);
  });
});

Bench — benchmarks intégrés

import { bench, describe } from 'vitest';

describe('performance de tri', () => {
  const data = Array.from({ length: 1000 }, () => Math.random());

  bench('Array.sort natif', () => {
    [...data].sort((a, b) => a - b);
  });

  bench('quicksort maison', () => {
    quickSort([...data]);
  });
});

Lancez avec npx vitest bench pour voir les résultats :

bench > performance de tri
  Array.sort natif     2,847 ops/sec ± 1.23%  (fastest)
  quicksort maison     1,203 ops/sec ± 2.45%

Type checking avec expectTypeOf

Vitest inclut un système de vérification de types pour s'assurer que votre TypeScript est correct :

import { expectTypeOf } from 'vitest';
import { parseUser } from './parser';

it('retourne le bon type', () => {
  const user = parseUser('{"name":"Alice","age":30}');
  
  expectTypeOf(user).toEqualTypeOf<{ name: string; age: number }>();
  expectTypeOf(user.name).toBeString();
  expectTypeOf(user.age).toBeNumber();
});

// Vérifier les signatures de fonctions
it('la fonction accepte les bons paramètres', () => {
  expectTypeOf(parseUser).parameter(0).toBeString();
  expectTypeOf(parseUser).returns.toEqualTypeOf<{ name: string; age: number }>();
});

Tests React avec Vitest et Testing Library

// src/test/setup.ts
import '@testing-library/jest-dom'; // Étend les matchers

// vite.config.ts
// setupFiles: ['./src/test/setup.ts']
// src/components/__tests__/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { Button } from '../Button';

describe('Button', () => {
  it('affiche le label', () => {
    render(<Button label="Cliquer ici" onClick={vi.fn()} />);
    expect(screen.getByText('Cliquer ici')).toBeInTheDocument();
  });

  it('appelle onClick au clic', () => {
    const handleClick = vi.fn();
    render(<Button label="Cliquer" onClick={handleClick} />);
    
    fireEvent.click(screen.getByRole('button'));
    
    expect(handleClick).toHaveBeenCalledTimes(1);
  });

  it('est désactivé quand la prop disabled est true', () => {
    render(<Button label="Désactivé" onClick={vi.fn()} disabled />);
    
    expect(screen.getByRole('button')).toBeDisabled();
  });
});

Gestion des modules ESM

C'est là où Vitest brille vraiment. Les modules ESM sont supportés nativement, sans transformation Babel :

// ✅ Import dynamique — fonctionne nativement
it('charge le module dynamiquement', async () => {
  const { processData } = await import('./processor');
  expect(processData('input')).toBe('processed: input');
});

// ✅ Top-level await dans les fichiers de config
// vitest.config.ts peut utiliser des imports ESM directs

Pour mocker des modules ESM, la syntaxe est légèrement différente :

// Pour les modules ESM purs, utilisez vi.mock avec factory
vi.mock('./esm-module', async (importOriginal) => {
  const original = await importOriginal<typeof import('./esm-module')>();
  return {
    ...original, // Garder les exports non mockés
    fetchData: vi.fn().mockResolvedValue({ data: 'mocked' }),
  };
});

Quand garder Jest ?

Vitest n'est pas toujours le bon choix. Gardez Jest si :

  • Vous n'utilisez pas Vite et la migration représente un coût élevé sans bénéfice immédiat
  • Votre écosystème de transformers est complexe (Babel avec des plugins très spécifiques)
  • Vous avez des workers ou des configurations Jest très custom qui n'ont pas d'équivalent direct
  • Votre équipe est 100% satisfaite de Jest et n'a pas de problème de performance

Les deux frameworks sont excellents. Vitest est le choix naturel pour les nouveaux projets Vite, Jest reste pertinent pour les projets établis avec un écosystème mature.

Conclusion

Vitest a atteint une maturité remarquable. Son mode de compatibilité Jest permet la migration de la plupart des suites existantes en quelques minutes, et ses fonctionnalités exclusives (UI intégrée, benchmarks, expectTypeOf, ESM natif) en font un outil véritablement moderne.

Si vous démarrez un nouveau projet avec Vite — qu'il soit React, Vue, Svelte ou Node.js — Vitest est le choix évident. Pour les projets Jest existants, mesurez d'abord votre temps de build : si vos tests prennent plus de 30 secondes à démarrer, la migration vous fera gagner du temps dès le premier mois.