見出し画像

JestからVitestへ:移行手順とパフォーマンス検証


パフォーマンス:数字で見る現実

Vitestは大規模なテストスイートにおいて、コールドスタートで5.6倍高速(38秒 vs 214秒)、ウォッチモード再実行で28倍高速(0.3秒 vs 8.4秒)、ピークメモリ使用量を57%削減するベンチマークを実現しています。これは単なる「若干高速」ではなく、開発ワークフローの質を根本的に変える規模の改善です。

意外な点として、Jest 30のリリース(2026年2月)でもCJS優先のアーキテクチャと実験的なESMフラグのままで、根本的な設計は維持されているのに対して、Vitestは最初から異なる哲学で設計されています。

ESMサポート:「実験的」と「ネイティブ」の落とし穴

Jest 30でも、ESMサポートはなお「実験的」で、--experimental-vm-modulesフラグと慎重な設定が必要であり、chalk v5やnanoid、node-fetch v3といったESM専用パッケージはtransformIgnorePatternsの工夫やCommonJS互換性シムを強制しています。

Jest中心の開発では、ESM専用パッケージの利用時に複雑な設定回避が必要になるのに対して、Vitestは互換性シムを使わず、Viteのモジュールパイプラインを通すため、import.meta、トップレベルのawait、.mjsファイルが何のフラグなく、何の回避策もなく機能します。

一般には知られていない問題として、Jest では moduleNameMapperエントリをESM専用パッケージの対応だけで14個抱えていたプロジェクトが、Vitestへの移行でそれらすべてを削除できたという実例もあります。

設定の複雑性:共有できるものとできないもの

Vitestの最大の利点は設定の削減です。Vitestは既存のvite.config.tsを再利用でき、同じエイリアス、プラグイン、トランスフォーメーションが適用されるため、重複した設定がないという特徴があります。

これはVite採用プロジェクトにおいて別のmoduleNameMapperセットアップが不要という意味で、vite.config.tsで設定したエイリアスや環境変数がテスト環境に自動的に継承されることを意味します。

一方Jestでは、本番ビルドと異なる変換パイプラインが動作するリスクが存在します。Viteで構築されたアプリの場合、babel-jestはテストパスの一方でバベル変換し、本番ではVite変換が行われるため、「開発時は通るがJestで落ちる」というESMの問題が根本的に生じるという設計上の問題があります。

エコシステムの採用状況:傾きが急激に変わった

Jest は約3000万の週次npmダウンロード、Vitestは約2000万へと急騰し、このトレンドから見ると12~18ヶ月以内にダウンロード数が同等になる可能性があるという急激なシフトが起きています。

より重要な指標として、直近6ヶ月の新規プロジェクト設定(--save-dev install count)では、Vitestが新しいTypeScriptプロジェクトの約65%を占めているという点があります。過去のノスタルジアではなく、現在のメインストリームの選択です。

Nuxt、SvelteKit、Astro、Angularの最新ツールは標準で、またはVitestを推奨として同梱しているため、メタフレームワークレベルでVitestが標準化されています。

実践的な移行:APIレベルでの互換性

意外かもしれませんが、Vitestとjestのテストコードはほぼ互換性があります。describe、it、expect の基本API は同一であり、以下は実際の移行を示す簡潔な例です:

// Jest設定
// jest.config.js (複雑な変換パイプライン必須)
module.exports = {
  preset: 'ts-jest',
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
  },
  transformIgnorePatterns: ['node_modules/(?!(nanoid)/)'],
  testEnvironment: 'jsdom',
};

// テストコード (Jest)
import { Button } from '@/components/Button';
describe('Button', () => {
  it('renders', () => {
    expect(true).toBe(true);
  });
});
// Vitest設定
// vitest.config.ts (vite.config.ts を extends)
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: { '@': path.resolve(__dirname, './src') },
  },
  test: {
    environment: 'jsdom',
  },
});

// テストコード (Vitest - ほぼ同じ)
import { Button } from '@/components/Button';
describe('Button', () => {
  it('renders', () => {
    expect(true).toBe(true);
  });
});

設定の行数はJestで15~20行、Vitestで10行以下まで削減され、変換設定の複雑さもほぼ完全に排除されます。

移行コストの現実的な評価

CI パイプラインが遅い、またはチームがTDDで高速なフィードバックループを練習している場合、Vitestへの移行はその対価をすぐに回収するという判断基準があります。

移行の計算式は2つの要素に依存します。CIでのテスト実行が2分以上かかる場合、Vitestにより30秒未満に短縮される可能性が高く、また依存関係の更新後のJest設定デバッグに定期的に時間を費やしている場合、Vitestはそのメンテナンス負担を完全に排除します。

現実的なアドバイス

新規プロジェクトを開始する場合はVitestを選ぶ、既存のJestプロジェクトを保守し、パフォーマンスが十分な場合はJest 30に留まり、コスト効果が意味をなす時点で移行する、ただしCI パイプラインが遅いかTDD開発である場合は移行が早期に対価を回収するという判断が実装チームの多くで採用されています。


Jestはこの10年近く、JavaScriptテスティングの標準として機能してきました。しかし、ESM→コモンジャパラダイムの移行やViteの台頭により、テスティングフレームワークの役割そのものが変わりました。Vitestへの移行は単なる「新しいツール」の選択ではなく、現代的なJavaScriptスタックとの調和を取り戻すプロセスです。

ここから先は

11,948字

¥ 300

この記事が気に入ったらチップで応援してみませんか?