VitestでJestからユニットテストを乗り換えたら、テスト実行がかなり速くなった話
Jestで動いていたユニットテストをVitestに移行したところ、設定が簡潔になりテスト実行速度がかなり上がりました。移行手順と感想をまとめます。
🙌 結論から
ViteプロジェクトのユニットテストをJestからVitestに移行したところ、テスト実行が体感で2〜3倍速くなり、設定ファイルもかなりシンプルになりました!
Jestはデフォルトでは ESModule の import がそのまま動かないため、Viteプロジェクトで使うには babel-jest や ts-jest の追加設定が必要でした。
VitestはViteの設定をそのまま共有できるので、パスエイリアスなどの設定を二重に書く必要がありません。
Jest互換のAPIで書かれているので、既存のテストコードはほぼそのまま使い回せました(^o^)/
💡 Jestを使っていたときの問題

仕事で使っているViteプロジェクトで、ユニットテストにJestを使っていました。
JestはデフォルトではESModuleの import をそのまま扱えないため、ts-jest を導入して変換する設定が必要でした(´・ω・`)
// jest.config.ts(設定が複雑になりがちだった)
export default {
preset: 'ts-jest',
testEnvironment: 'node',
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
}
Viteでパスエイリアス(@/)を設定していたので、moduleNameMapper でその設定をJest側にも重複して書く必要がありました。
同じ設定を2箇所に書くのはメンテナンスコストが上がります。
実際に一度、パスエイリアスをVite側だけ変更してJest側の設定を更新し忘れてテストが全部落ちる、という事故が起きました(・_・;)
テストが増えてくると実行時間も3〜4分かかるようになってきて、CIの待ち時間がわりと長く感じでいました…
👀 Vitestへの移行手順
まずパッケージを追加します。
npm install --save-dev vitest
vite.config.ts に test の設定を追加するだけで動き始めます!
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
test: {
environment: 'node', // Reactコンポーネントのテストなら 'jsdom'
globals: true, // describe/it/expect をインポートなしで使いたいなら
},
})
Viteの設定(パスエイリアスなど)が自動で引き継がれるので、Jestのような追加設定が不要です!
既存のテストファイルはほぼそのまま動きます。
jest.fn() や jest.spyOn() など jest グローバルを直接参照している箇所は、vi.fn()・vi.spyOn() に置き換える必要があります。
// Before (Jest)
const mockFn = jest.fn()
jest.spyOn(module, 'method')
// After (Vitest)
const mockFn = vi.fn()
vi.spyOn(module, 'method')
package.json のスクリプトを変更します。
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest"
}
}
vitest run が一回だけ実行して終了するモード、vitest がウォッチモードです。
✨ 移行してみた感想
移行前後でテスト実行時間を比較してみました。
# Jest(移行前)
Tests: 82 passed
Time: 3m 12s
# Vitest(移行後)
Tests: 82 passed
Time: 1m 04s
3分超かかっていたテストが1分ちょっとまで短縮されました(^o^)/
設定ファイルが jest.config.ts を丸ごと削除して vite.config.ts の数行に集約されたのも、かなりすっきりしていい感じです。
ウォッチモード(vitest)もかなり快適で、ファイルを保存すると関連するテストだけを即座に再実行してくれます!
一点だけ気になったのは、一部のモジュールモッキングの挙動がJestと微妙に異なるケースがあったことです。
特に vi.mock がファイルの先頭に自動でホイスティングされる挙動は、知らないとはまりやすいかもしれません(^^;)
公式ドキュメントの Mocking セクションを一度読んでおくと安心です。
🖥 UIモードとカバレッジも使い込んでみた
移行が一段落してから、Vitestの --ui オプションを試してみました。
npx vitest --ui
これを実行すると、ブラウザでテストの実行結果を見られるダッシュボードが立ち上がります。
どのテストが通って・落ちたか、それぞれの実行時間まで視覚的に確認できるので、大量のテストを実行したときに**「どれが遅いテストか」がひと目でわかる**ようになりました(・∀・)
ターミナルでズラッと結果が流れるのを目で追うより、UIモードで眺めながら失敗したテストだけクリックして詳細を見る方が、デバッグの体験としてかなり快適です。
もう一つ使うようになったのがカバレッジ計測です。
npm install --save-dev @vitest/coverage-v8
// vite.config.ts
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
},
},
})
vitest run --coverage を実行すると、V8のネイティブなカバレッジ計測を使って、どの行がテストされていないかをHTMLレポートで確認できます。
Jestのときは babel-plugin-istanbul を使ったカバレッジ計測がやや重く感じていたんですが、V8ネイティブの計測に変わってから体感でも速くなった印象です。
「テストは書いているつもりでも、意外とこの分岐は通っていなかった」という発見がカバレッジレポートから何度かあって、テストの抜け漏れに気づくきっかけになっています!
パパ系Webエンジニア〜愛用ガジェット一覧 🛍️
この記事を書いている作業環境のガジェットを楽天ROOMにまとめています ✨ 在宅エンジニアのデスク作りの参考にどうぞ 🙌
👍 まとめ
VitestはViteプロジェクトとの相性がとにかく良く、設定が楽なのにテスト実行速度は大幅に上がるという、費用対効果の高い移行でした。
Jest互換のAPIなので既存のテストをほぼ書き換えずに済み、移行コストは思ったより低かったです。
Viteを使っているプロジェクトでまだJestを使っているなら、ぜひ一度Vitestへの切り替えを検討してみてください。
テストを書く習慣が定着すると、コードへの自信がかなり変わりますね(^^)
テストはあまり書いてこなかったので、めっちゃいい経験になりました!
※ Vitestのバージョンによって仕様が変わることがあります。最新情報は Vitest公式ドキュメント をご確認ください👀