Framework🛠️ 開発体験(DX)

ナレッジワーク

ナレッジワーク、フロントエンドのテスト基盤をJestからVitestへ。ESM互換性の壁を越えるため足掛け3日で移行したが、CI上の実行時間は約1.5倍に

Jest(next/jest)Vitest

ナレッジワークは、Next.jsを採用したフロントエンドのテストフレームワークをJestからVitestへ移行した。JestはCJSを前提として動作しており、ページなど上層のテストを書こうとするとESM互換性の問題を踏んでしまうことが理由だ。Vitest v1.0.0のリリースを機に、調査やレビューを含めて足掛け3日ほどで移行を終えた。pnpm-lock.ymlの差分は682 additions / 1,109 deletionsで依存は身軽になった一方、ローカル実行は体感できるほどの差がなく、CI(GitHub Actions)上での実行は約1.5倍の時間がかかるようになった。

課題

JestがCJS前提で、ページなど上層のテストを書くとESM互換性の問題を踏み、テストを書くのが億劫な状態が続いていた

アプローチ

Vitest v1.0.0のリリースを機に移行。SWCプラグインや@next/env等で設定を整え、Storybookのテストも同時に移行

効果

足掛け3日で移行し依存も身軽に。ただしCI上の実行時間は約1.5倍に増え、原因は不明のままとしている

背景

ナレッジワークのフロントエンドはNext.jsを採用しており、テストフレームワークにはNext.jsと相性の良いJestを採用していた。関数単位のテストやUIコンポーネントのテストを書く分には問題なかったが、それより上層、たとえばページ単位になると、たちまちESM互換性の問題を踏んでしまう。JestはCJSを前提として動作しており、ESM前提のモジュールを動かすのに一手間も二手間もかかるからだ。そのため、テストを書こうにも腰の重い状態が続いていた。そんな中、ある機能でページ単位のテストを重点的に書く必要性が生まれた。ちょうどVitestのv1.0.0がリリースされたこともあり、移行するには絶好のタイミングだとして、この機会にVitestへ移行することになった。

移行のプロセス

移行は基本的に公式ドキュメントの手順どおりに進めたが、プロジェクト固有の事情で公式に書かれていない部分もあった。

  • 依存の更新: jest、@types/jest、jest-environment-jsdom、@storybook/jestなどを削除し、vitest、@vitejs/plugin-react-swc、jsdom、vite-tsconfig-paths、@next/env、@storybook/testを追加
  • plugin-reactの代わりにplugin-react-swcを使い、Babel部分のトランスパイルを高速化
  • next/jestをやめたため、@next/envで環境変数を自前でセット
  • vite-tsconfig-pathsでインポートのエイリアス設定をtsconfig.jsonから読み込む
  • happy-domは一部の動作に不具合があったため見送り、jsdomを採用
  • testTransformModeを、Vitest 0.33→0.34で起きたパフォーマンス劣化の対策として設定
  • jsdomで未実装のメソッドなどはセットアップファイルでモック

同時期にリリースされたStorybook 7.6で@storybook/testがVitestを使うように変更されたため、@storybook/jestからの移行も合わせて行った。

移行前後の変化

指標BeforeAfter
テストフレームワークJest(next/jest)Vitest
トランスパイラNext.jsと同じSWCVite(esbuild)+ plugin-react-swc
Storybookのテストユーティリティ@storybook/jest@storybook/test
pnpm-lock.ymlの差分—682 additions / 1,109 deletions
ローカルでのテスト実行時間Jest体感できるほどの差なし
CI(GitHub Actions)上の実行時間Jest約1.5倍
移行期間—調査やレビューを含めて足掛け3日ほど

結果

調査やレビューを含め、足掛け3日ほどで移行が完了した。

  • 直接の依存の数は増えたが、pnpm-lock.ymlの差分は682 additions / 1,109 deletionsで、だいぶ身軽になった
  • ローカル実行はJestとVitestで体感できるほどの差がなかった
  • CI(GitHub Actions)上での実行は約1.5倍の時間がかかるようになった。原因はわかっていない

それでも、CIのボトルネックはVRT(Visual Regression Test)の方であることや、CIの最適化の余地がまだあることを勘案し、全体としてはVitestに乗っておいた方が良いという結論に至った。これまでテストを書けなかった領域への道が開かれたとしている。

学び

高速化したという声も多いVitestだが、この事例ではCI上の実行時間がむしろ延び、実行環境やコードベースの条件次第だと振り返っている。また、Next.jsはSWC、Viteはesbuildでトランスパイルするため、厳密には本番で使われるコードをテストできない。同じモジュール内でexportされる関数をvi.mock()で差し替えても、別の関数内での呼び出しは置き換わらないというJestとの挙動差にも注意が必要だ。

2023-12-26
他の事例も見る