Jest から Vitest 移行の対応まとめ
仕事で開発中のシステムのテスト環境を Vitest に移行しました
注意点として、Vitest はまだかなり新しく、Jest と比べ安定してるとは言えないライブラリです
以前は Vitest の GitHub の README に本番環境での利用は非推奨の文言がありました
(このコミットで削除されたようですが、バージョンもまだ v0 系なのでしばらくは破壊的変更やバグもそれなりに出る可能性はあります)
移行を決断したポイントは以下です
- Jest(webpack)のパフォーマンス改善に限界を感じた
- 現システムで GitHub へ Push するたびに Build、Lint、テストなどを行う CI が走るが、システムの規模が大きく完了に毎回 20 分程度かかる(そのうちテストは 7〜8 分)
- UI のテスト(Storyshots)は引き続き Jest で行う
- Storyshots は現状、Jest のみで動くため
- 移行の対象を UI 以外にフォーカスしたことでハードルがだいぶ下がった
- Vitest は Jest と互換性が高いので、修正箇所が少なそうだった
結果的に UI 以外のテストを Vitest に移行したところ、CI のテストは 5 分程度になりました
一応この移行のゴールは Jest 依存の完全排除なので、対応が進んだらまたこのスクラップ記事に追記します
環境は全体的に少し古いですが、以下です
- vitest
- v0.7.7
- typescript
- v3.9.2
- Node
- v14.19.1
Node は v14 系だと v14.19.0 以上が必須でした(vitest が v0.7.7 の場合)
現在最新の vitest v0.9.3 だと Node v14.16.0 が必須になります(また変わるかもしれません)
Node は v16 系とかを使っておくのが安定かなと思います
typescript も v3 系ですが、vite か vitest の型定義で Template Literal Types を使っている箇所があったので、型チェックも行うなら typescript v4.1 以上が必要になります
Storyshots が Vitest で動かない
現状はまだ動かせず、以下のエラーになる
Error: testStorySnapshots is intended only to be used inside jest
関連 GitHub Issue
Storybook を Vite で動かす builder は公式で開発中なので、そのうち Storyshots が Vitest で動くプラグインも作られるのではないかなと思っています
Jest のグローバルなオブジェクトや関数
describe や it など
これらはそのままグルーバルに使用したい
以下を設定ファイルに書く
import { defineConfig } from "vite";
export default defineConfig({
test: {
globals: true,
},
});
テストコードで jest を直接呼んでるやつ
たとえば、以下のようにテストコードで直接 jest を書いている
jest.mock(...);
jest.fn(...);
beforeAll(() => {
jest.useFakeTimers();
jest.setSystemTime(new Date("2000-01-01 00:00:01"));
});
afterAll(() => {
jest.useRealTimers();
});
以下で置換できる
import { vi } from "vitest";
vi.mock(...);
vi.fn(...);
beforeAll(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2000-01-01 00:00:01"));
});
afterAll(() => {
vi.useRealTimers();
});
排除できない Babel 依存
将来的には排除したいが、今は難しい Babel プラグインは以下で対応
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [
react({
babel: {
plugins: ["babel-plugin-macros"],
},
}),
],
});
(現状、ビルド環境も Babel 依存の webpack なので、Vite にしたい)
resolve alias の設定
alias を使っている場合は以下で設定できる
import path from "path";
import { defineConfig } from "vite";
export default defineConfig({
resolve: {
alias: {
"@s": path.join(__dirname, "../shared/src"),
},
},
});
もし正規表現を使いたい場合は、上記の書き方だと使えないので、以下にする必要がある
import path from "path";
import { defineConfig } from "vite";
export default defineConfig({
resolve: {
alias: [
{ find: "@s", replacement: path.join(__dirname, "../shared/src") },
{
find: /.+\.(jpg|jpeg|png|gif)$/,
replacement: path.join(__dirname, "../__mocks__/fileMock.js"),
},
],
},
});