Retour au Blog
Angular Development20 mai 20259 min de lecture

Migrer de Jest vers Vitest dans un projet Angular

DR

Doug Ribeiro

9 min de lecture

Mise à jour, septembre 2026 : lisez ceci en premier si vous êtes sur une version récente d'Angular. Angular 21 livre @angular/build:unit-test avec Vitest comme runner par défaut, et l'équipe Angular publie un guide de migration à https://angular.dev/guide/testing/migrating-to-vitest. Sur Angular 20 ou 21, utilisez ce builder plutôt que la configuration manuelle ci-dessous : vous n'avez besoin ni du plugin Analog, ni d'un vitest.config.ts, ni de votre propre fichier test-setup. Tout ce qui va de l'étape 1 à l'étape 5 ici est la recette pour Angular 19 et antérieur, écrite en mai 2025. Les notes sur l'API de Jest vers Vitest plus bas s'appliquent dans les deux cas.

Jest me coûtait du temps réel. Sur MailHive, le produit SaaS que je construis en solo, la suite complète du front-end Angular prenait bien plus d'une minute en local et plusieurs minutes en CI GitLab. Chaque merge request avait une taxe de plusieurs minutes juste pour voir du vert. J'ai basculé vers Vitest et réduit les deux à une fraction. Voici exactement ce que j'ai fait.

Pourquoi Jest devient lent

Jest a été conçu pour CommonJS et repose sur Babel ou ts-jest pour transpiler le TypeScript avant de lancer quoi que ce soit. Cette transformation se produit à chaque exécution à froid. Vitest est construit sur Vite, qui utilise esbuild pour les transformations et ESM natif. Il exécute aussi les fichiers spec en parallèle par défaut (des processus enfants depuis Vitest 2, des threads de travail si vous l'activez) et maintient le graphe de modules de Vite pour n'exécuter que les tests affectés par vos changements en mode watch. La différence est structurelle, pas cosmétique.

Pipeline JestSource TypeScriptTransformation ts-jest / BabelSortie CommonJSRunner Jest (pool de workers, l…Plus d'une minute en local, plu…Démarrage à froid à chaque exéc…Pipeline VitestSource TypeScriptTransformation esbuild (native)Sortie ESM nativeWorkers parallèles par défautQuelques secondes en local, une…HMR Vite. Relance instantanée a…VS

Étape 1 : remplacer les dépendances

bash
# Remove Jest
npm uninstall jest jest-environment-jsdom @types/jest ts-jest jest-preset-angular

# Install Vitest + Angular plugin
npm install --save-dev vitest @vitest/coverage-v8 jsdom @analogjs/vite-plugin-angular

Le package @analogjs/vite-plugin-angular gère la compilation Angular dans Vite. Sans lui, le compilateur de templates et les décorateurs Angular ne fonctionneront pas dans l'environnement Vitest.

Étape 2 : vitest.config.ts

typescript
import { defineConfig } from 'vitest/config';
import angular from '@analogjs/vite-plugin-angular';

export default defineConfig({
  plugins: [angular()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: ['src/test-setup.ts'],
    include: ['src/**/*.spec.ts'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'lcov'],
      include: ['src/app/**/*.ts'],
      exclude: ['src/app/**/*.spec.ts', 'src/app/**/*.module.ts']
    }
  }
});

globals: true est important. Ça rend describe, it, expect, vi disponibles sans imports, correspondant à l'API Jest que vos tests existants attendent.

Étape 3 : remplacer le fichier de setup de test

Supprimez setup-jest.ts ou src/test.ts. Remplacez par un src/test-setup.ts neuf qui initialise le TestBed d'Angular.

typescript
// src/test-setup.ts
import '@angular/compiler';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';

getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
);

Une note de version sur ce fichier : @angular/platform-browser-dynamic/testing était le bon import en mai 2025 et a été remplacé à partir d'Angular 20. Sur Angular 20 et plus, prenez plutôt BrowserTestingModule et platformBrowserTesting depuis @angular/platform-browser/testing. L'appel à initTestEnvironment est identique par ailleurs.

Étape 4 : mettre à jour tsconfig.spec.json

json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/spec",
    "types": ["vitest/globals"]
  },
  "include": [
    "src/**/*.spec.ts",
    "src/**/*.d.ts"
  ]
}

Supprimez @types/jest des types s'il est encore là. Avoir les deux cause des conflits de types.

Étape 5 : mettre à jour les scripts package.json

json
{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage"
  }
}

Le coût de migration : API Jest vers Vitest

Le travail réel de migration est un rechercher-remplacer global dans vos fichiers spec. Les APIs sont presque identiques par design.

  • jest.fn() devient vi.fn(). Faites un remplacement à l'échelle du projet de 'jest.' par 'vi.'
  • jest.mock() devient vi.mock(). Le comportement de hoisting est légèrement différent, consultez la doc Vitest sur le hoisting si vous utilisez des fonctions factory
  • Les snapshots sont repris pour l'essentiel. Vitest écrit un en-tête de fichier différent et formate quelques cas différemment (snapshots d'erreur, séparateurs de message personnalisés), attendez-vous donc à des diffs. Les régénérer une fois après la migration est la solution la plus simple
  • Les matchers @testing-library/jest-dom ont besoin de l'import vitest : import '@testing-library/jest-dom/vitest'
  • Zone.js doit toujours être importé dans votre fichier de setup. Le TestBed Angular s'y attend
  • fakeAsync, tick et TestBed sont d'Angular, pas de Jest. Ils fonctionnent sans changement

À quoi ça ressemblait sur un vrai projet

Le projet était le front-end Angular de MailHive : principalement des tests de services avec une bonne part de tests de composants utilisant TestBed, le tout bloquant les merges en CI GitLab derrière un seuil de couverture.

  • Jest avec ts-jest (à froid) : bien plus d'une minute en local
  • Vitest (à froid) : quelques secondes
  • Vitest (cache chaud) : des relances quasi instantanées en mode watch
  • GitLab CI runner partagé, Jest : plusieurs minutes par pipeline
  • GitLab CI runner partagé, Vitest : une fraction de ça, assez rapide pour que le job de test cesse d'être le goulot d'étranglement

Cette amélioration CI n'est pas qu'un chiffre. Ça veut dire que le retour arrive avant que vous ne changiez de contexte. Les merge requests avancent plus vite. Même en solo, on arrête de redouter le job de test.

Un piège qui mérite d'être signalé

Si des tests espionnent encore Date ou Math.random avec jest.spyOn, par exemple dans un helper de test partagé qui n'est pas un fichier spec et qui a échappé au rechercher-remplacer, ils échoueront parce qu'il n'y a pas de global jest dans Vitest. Remplacez par vi.spyOn. Aussi, vi.useFakeTimers() fonctionne pareil que jest.useFakeTimers() mais vous devez appeler vi.useRealTimers() dans afterEach si vous ne le faites pas déjà. J'ai eu quelques tests qui ont commencé à échouer de manière intermittente avant que je ne trace ça.

Des tests rapides changent les comportements. Quand la boucle de retour est instantanée, les développeurs lancent les tests avant de commiter. Quand ça prend des minutes, ils poussent en espérant que ça passe.

Prévoyez un après-midi pour un projet Angular typique. La majorité de ce temps est le rechercher-remplacer d'API et la chasse à une poignée de cas limites. Le retour sur investissement se compose à chaque exécution de test à partir de ce moment.

Références

AngularVitestTestingTypeScriptCI/CD