ナレッジワーク
ナレッジワーク、フロントエンドのテスト基盤をJestからVitestへ。ESM互換性の壁を越えるため足掛け3日で移行したが、CI上の実行時間は約1.5倍に
ナレッジワークは、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からの移行も合わせて行った。
移行前後の変化
| 指標 | Before | After | |
|---|---|---|---|
| テストフレームワーク | Jest(next/jest) | Vitest | |
| トランスパイラ | Next.jsと同じSWC | Vite(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との挙動差にも注意が必要だ。
