【フロントエンド】JestからVitestへの移行結果とガイド

media thumbnail

TL;DR

  • Jest から Vitest へのテストランナーの移行を行い、全体のテスト時間が大幅に短縮されました。
  • GitHub Actions でのUsageRunTimeは、Jest の32m35sに対してVitestは18m47sと、約14mの短縮が見られました。
  • ローカルテストの実行時間は最大210sの短縮が見られました。

前提・対象とする読者

  • テストの効率化とパフォーマンス改善を目指している開発者
  • Jest からのテストランナーの移行を検討している開発チーム

比較結果詳細:Jest vs Vitest

※ 比較時の前提条件として以下がありますのでご了承ください。テストケースの合計が1800件弱となっているためクリティカルに比較結果に影響しないものと判断して掲載しています。

  • JestからVitestへ移行する際、実装予定のない機能のテストケースを移行時に削除しました。そのため総テスト件数が19件ほど異なります
  • JestからVitestへ移行する際、実行結果が不安定なテストに関しては一時skip対応を行ないました。そのためJestのテストのpass件数が23件多くなっています

GitHub Actions

項目JestVitest短縮時間
Usage32m35s18m47s約14m
Duration8m10s3m46s約4.5m

※ Usageは各テストの実行時間の合計(並列を直列実行にするイメージ)で料金に直結し、Durationはテストの体感時間を示しています。

※ 画像はUsage比較

ローカルテスト

項目JestVitest短縮時間
全体テスト345s~379s169s~186s約160s

🔎 ローカルテストアウトプット比較

Jest(sequential & parallel)

※ Jestでは実行結果が不安定なテスト防止に並列実行と直列実行が混ざっています。
※ 🌱 は体感時間

=== 合計:230 + 149 = 379s
Test Suites: 58 passed, 58 total
Tests:       42 skipped, 68 todo, 1421 passed, 1531 total
Snapshots:   0 total
Time:        🌱230.462 s(parallel)
Test Suites: 8 passed, 8 total
Tests:       18 skipped, 12 todo, 236 passed, 266 total
Snapshots:   0 total
Time:        🌱149.118 s(sequential)
===
=== 合計:212 + 150 = 362s
Test Suites: 58 passed, 58 total
Tests:       42 skipped, 68 todo, 1421 passed, 1531 total
Snapshots:   0 total
Time:        🌱212.438 s
Test Suites: 8 passed, 8 total
Tests:       18 skipped, 12 todo, 236 passed, 266 total
Snapshots:   0 total
Time:        🌱150.576 s
===

Vitest

Test Files  73 passed (73)
      Tests  1634 passed | 81 skipped | 63 todo (1778)
   Start at  16:13:21
   Duration  🌱178.36s (transform 2.60s, setup 41.21s, collect 61.93s, tests 578.47s, environment 16.18s, prepare 4.28s)

Test Files  73 passed (73)
      Tests  1634 passed | 81 skipped | 63 todo (1778)
   Start at  16:16:55
   Duration  🌱169.16s (transform 2.71s, setup 41.15s, collect 54.73s, tests 556.54s, environment 8.55s, prepare 4.38s)

Test Files  73 passed (73)
      Tests  1634 passed | 81 skipped | 63 todo (1778)
   Start at  17:33:02
   Duration  🌱186.11s (transform 4.97s, setup 80.57s, collect 115.89s, tests 1056.40s, environment 14.76s, prepare 6.79s)

💨移行理由

移行理由はテストの実行時間が開発体験と開発コストに支障をきたしていたためです。
前提として私が所属するプロジェクトのテストケースは1500超に達しローカルのテスト実行に5~6m、git commitのたびにpre-commitのテスト実行による待ち時間の発生、CIの実行待ち時間が10m弱と諸々の理由で開発体験が悪くなっていました。
また他のプロジェクトに比べてGitHub Actionsの利用時間を多く使っており、このまま実装を進めていくことでコストが増え続ける懸念がありました。

📖移行ガイド

  1. パッケージのインストール
  2. 設定ファイル
  3. グローバルな型補完
  4. マッチャーの拡張
  5. 環境変数の設定

1. パッケージのインストール

Vitest Getting Started

以下のパッケージをnpm等でインストールします。
※ 移行時点でのバージョンを記載していますのでお手元の環境に合わせて変更してください。

"happy-dom": "^13.7.8", // node環境でDOM環境を再現
"vitest": "^1.3.1", // vitest本体
"@vitejs/plugin-react": "^4.2.1", // Reactコンポーネント利用時に必要
"@testing-library/jest-dom": "6.4.2", // Matcherの拡張

2. 設定ファイル

Managing Vitest config file
Configuring Vitest

基本的なことは公式ドキュメントに書いてありますので特筆すべきところのみ紹介します。

  • environment: テスト環境を指定します。DOM環境を再現したい場合はjs-domhappy-domを指定します。これによってテスト内でdocument.querySelectorやtesting-library等のDOM用の取得メソッドが使用できるようになります。
  • globals: Jestのようにグローバルにメソッド(describe,it, etc…)を利用したい場合指定が必要です。指定しない場合、CLIで引数を渡すか、vitestから明示的にimportして使用する必要があります。
  • alias: テスト実行対象ファイル内で参照しているモジュールのパス解決を行います。Nextjsなどでパスエイリアスを設定している場合は指定が必要になります。

他にもsetupファイルの指定やtimeoutの指定等可能なのでご自身の環境に応じて追加で設定してみてください。

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

余談ですが、私が所属するプロジェクトではDOM環境の再現にhappy-domを使用しています。DOM環境の再現用パッケージはいくつかありますが、js-domhappy-domの2択になっている印象です。happy-domjs-domに比べて使用できるAPIが少ないが高速という特徴があります。現在プロジェクトのフロントのテストケースは1500超ですがとくに不便は感じておらず、快適にテストライフを送ることができています。

3. グローバルな型補完

tsconfigに以下の設定を追加することでvi.mock等の型補完を効かせることが可能です。

"compilerOptions": {
    ...,
    "types": [
      ...
      "vitest/globals",
    ]
  },

4. マッチャーの拡張

VitestのマッチャーにはデフォルトではtoBeInTheDocument等は用意されていません。これらのマッチャーを使用したい場合マッチャーの拡張を行う必要があります。
デフォルトで使えるマッチャーは以下を確認ください。

expect

設定は以下を参考にしても行えます。

With Vitest

Vitestのsetupファイルに以下を追記します。

※ setupファイルは任意のディレクトリに作成してください。例:src/test/setup.ts

import '@testing-library/jest-dom/vitest';

setupファイルはvitest.config.tsファイルに読み込ませます。

export default defineConfig({
  plugins: [react()],
  test: {
    ...,
    setupFiles: ['/path/to/setup.ts'],
  },
});

最後にマッチャー用の型を拡張してください。グローバルにマッチャーの型補完が効くようになります。

"compilerOptions": {
    ...,
    "types": [
      ...
      "@testing-library/jest-dom",
    ]
  },

※ マッチャーの型情報がjest.Expectになりますがこのライブラリを使用して拡張する以上仕方ないかと思います。

5. 環境変数の設定

Vitestではvitest.config.tsファイルやCLIでの引数指定が可能です。
今回はテストファイル内で環境変数を参照したいときという要件に絞って紹介します。
グローバルセットアップファイルではテスト実行の最初と最後に指定の関数が実行されます。それを利用して環境変数に値をセットします。

type Env = {
  [key: string]: string;
};

export const env: Env = {
  EXECUTION_BY: 'local',
};

Object.keys(env).forEach((key) => {
  process.env[key] = env[key];
});

/**
 * INFO: setupとteardownのexportが必須のためconsoleのみ実行
 */
export const setup = (): void => {
  console.log('Global Setup was called.');
};

export const teardown = (): void => {
  console.log('Global Teardown was called.');
};

私の所属するプロジェクトではローカルではパスするが GitHub Actions 等のCIでは落ちてしまう不安定なテストが存在します。その不安定なテストをCIでスキップするために環境変数でローカル実行なのかCI実行なのかわかるようにしています。

たとえば以下のように使用します。

// EXECUTION_BYの値がlocalの場合実行
it.runIf(process.env.EXECUTION_BY === 'local')(
  'テストケース',
  async () => {
    //...
  }
);

※ 以下のようにconfigファイルでもenvを設定できますが、テストファイルの中で参照することができません。申し訳有りませんが理由は調査しても見つけきれませんでした。

export default defineConfig({
  ...,
  test: {
    ...,
    env: {
      EXECUTION_BY: 'local'
    },
  },
});

🚧移行過程での課題

テストの謎落ち

インプットの入力が絡むテストとStoryBookが絡むテストで謎に落ちるテストが稀にありましたがテストを別ファイルに分割することで解決しました。

以下のissueは関連していそうですがはっきりとした理由はまだわかっていないため引き続き調査が必用です。

testing-library/react: cleanup not called automatically with isolate: false

余談ですが、私のチームではStorybookをテストで使用していました。playメソッドでStoryの振る舞いをテストで再利用しているイメージです。Storybook8ではVitestサポートが強化され、storybook/testも公開されたためこれらを利用するように移行も行いました。

GitHub Actionsのキャッシュ残り

移行後CIでテストを実行した際にテストが失敗してしまう事象が発生しました。よくよく確認するとJest時代のnode_modulesのキャッシュが残っておりパッケージはそこを参照していたためでした。キャッシュからnode_modulesを削除することで対応しました。

以上になります。同じような移行を検討している他のプロジェクトチームにとっての参考になれば幸いです。

少しでも開発にお困りの方は
相談しやすいスペシャリストにお問い合わせください

お問い合わせ
  1. breadcrumb-logo
  2. メディア
  3. 【フロントエンド】JestからVitestへの...