テスト

Vitest 5移行ガイド:4からの破壊的変更と落ちるテストの直し方【2026年10月時点】

Vitest 5移行ガイド:4からの破壊的変更と落ちるテストの直し方【2026年10月時点】

Vitest 5.0.0 は 2026年9月3日に npm へ公開され、10月12日時点の最新は 5.0.3 です。4.1系から上げると、モックの呼び出し回数を数えるテスト、await を書き忘れた resolves、test.sequential を使ったテスト、ブラウザモードの getByText が落ち始めます。CI の成果物についても、出力先が変更の対象です。この記事では公式の移行ガイドに載る破壊的変更を「どのテストが落ちるか」「どう直すか」の単位で並べ直し、上げる時期の判断までを扱います。Vitest 4 の新機能と 3 から 4 への移行はVitest 4の新機能と3から4への移行ガイドにまとめています。

まとめ:Vitest 5移行で先に直す箇所と後回しでよい変更の線引き

最初に確かめるのは動作要件です。Node.js 22.12.0 以上と Vite 6.4.0 以上を満たさないと、他の修正に入る前にエラーで止まります。Node.js 20 系の CI ランナーを使っているなら、ランナーの更新が移行作業の起点になります。

テストが落ちる変更は5種類に絞れます。clearMocks の既定化、トップレベル以外の vi.mock、await のない非同期アサーション、sequential の削除、ブラウザモードのロケーターと toHaveTextContent の完全一致化です。いずれもエラー位置が出るため、一度全件を走らせて赤くなった順に直すのが早道です。レポート出力先の .vitest 集約は CI 側の設定変更で、テストコードには影響しません。ベンチマークを書いていないプロジェクトは、ベンチマーク API の再設計を読み飛ばして構いません。

Vitest 5.0公開日と要件引き上げ|Node.js 22.12・Vite 6.4

5.0.0公開から5.0.3までの版の流れと4.1系に残る保守の位置

公式ブログの Vitest 5.0 告知の日付は 2026年9月3日で、npm registry の公開時刻とも一致します。その後 9月15日に 5.0.1、9月25日に 5.0.2、9月30日に 5.0.3 が出ており、約1か月で3回のパッチが重なりました。変更の全量は GitHub の v5.0.0 リリースノートで追えます。

4 系は npm の dist-tag V4 が 4.1.11(2026年8月18日公開)を指しています。3 系にも V3 タグが残っており、旧メジャーをタグで指名して入れられる運用です。ただし 4.1.11 以降のパッチが出るかどうかは告知されていません。5.0 公開から日が浅い現時点では、移行の検証ブランチを切りつつ、本番の CI は 4.1.11 に固定しておく二段構えが無理のない形です。

Node.js 22.12以上とVite 6.4以上を満たすか確かめるコマンド

5.0.3 の package.json は、engines.node を ^22.12.0 || ^24.0.0 || >=26.0.0 と宣言しています。Node.js 20 系は対象外で、23 系と 25 系も含まれません。Vite は peerDependencies で ^6.4.0 || ^7.0.0 || ^8.0.0 です。各系列の保守状況は Node.js のリリース一覧と Vite のリリースページで確認できます。

# 手元とCIの両方で実行し、要件を満たすか確かめる
node -v                      # v22.12.0 以上、または v24 系・v26 以上
npm ls vite                  # 6.4.0 以上か(7 系・8 系も可)
npm view vitest@latest engines peerDependencies.vite

npm ls vite が複数の版を返す場合は、Storybook や Nuxt など別パッケージが古い Vite を抱えています。Vitest がどちらを解決するかで結果が変わるため、版を1つに揃えてから進める手順です。Vitest と Vite の関係そのものはVitestの仕組みと前提環境を解説した記事で整理しています。

依存の更新と設定ファイルの確認|CIで全テストを走らせるまでの手順

package.jsonの更新とYarn利用時にviteを明示的に追加する作業

5 では vite が Vitest の直接の依存から peer 依存に移りました。npm と pnpm は peer 依存を自動で入れますが、Yarn は入れません。移行ガイドの Yarn の項のとおり、Yarn を使うプロジェクトでは vite を自分で devDependencies に加えます。

# npm の場合(@vitest/ 配下のパッケージも同じメジャーに揃える)
npm install -D vitest@^5 @vitest/coverage-v8@^5

# Yarn の場合は vite の明示追加が要る
yarn add -D vitest@^5 vite@^7

@vitest/browser-playwright や @vitest/ui を入れている場合は、同じコマンドに並べて 5 系へ上げます。@vitest/browser-webdriverio はコミュニティ管理へ移ったため、WebdriverIO を使うプロジェクトは更新の追随状況を個別に確認してください。

設定ファイルの親ディレクトリ探索の廃止と–config指定への書き換え

4 までは、サブディレクトリでコマンドを打つと親ディレクトリの vitest.config.ts を探しに行きました。5 ではこの探索をやめています。モノレポで packages/app に移動してから npx vitest を実行する手順書や npm scripts は、設定が見つからないまま既定値で動き、エイリアスや setup ファイルが効かない形で失敗します。

直し方は、ルートの設定を明示することです。npx vitest --config ../../vitest.config.ts のように --config を付けるか、各パッケージに設定ファイルを置きます。同じく、外部から読み込む resolveConfig の戻り値も変わりました。4 の { vitestConfig, viteConfig } ではなく解決済みの Vite 設定が返り、Vitest の設定は viteConfig.test から取ります。自作のツールでこの関数を呼んでいる場合だけ影響します。

モックで落ちるテストの直し方|clearMocks既定化とvi.mockの配置

clearMocksの既定trueで呼び出し回数のアサーションが崩れる条件

移行ガイドの clearMocks の項によると、5 では各テストの前に vi.clearAllMocks() 相当が走ります。消えるのは呼び出し履歴だけで、mockReturnValue などで与えた実装は残ります。落ちるのは、setup ファイルや beforeAll で発生した呼び出しを、後続のテストで数えている書き方です。

import { beforeAll, expect, test, vi } from 'vitest'

const logger = { info: vi.fn() }

beforeAll(() => {
  logger.info('boot')          // ここでの呼び出しは各テストの前に消える
})

test('起動ログが1回出ている', () => {
  // 4 では通り、5 では 0 回となり失敗する
  expect(logger.info).toHaveBeenCalledTimes(1)
})

直し方として選べるのは次の2通りです。呼び出しを数える処理をそのテストの中へ移すのが本筋で、テスト同士の依存も同時に消えます。一時的に 4 の挙動へ戻すなら設定に clearMocks: false を書きます。テスト間で履歴が漏れて別のテストを誤って通していたケースが見つかるので、恒久的には既定値のまま直すほうが得です。React Testing Library と組み合わせたモックの書き方はVitestとTesting LibraryでReactをテストする手順で扱っています。

vi.mockをトップレベル以外で呼ぶエラーと該当箇所を洗い出す方法

vi.mock・vi.unmock・vi.hoisted はファイルの先頭へ巻き上げられる関数です。4 では describe や beforeEach の中で呼んでも警告で済みましたが、5 ではエラーで止まります。巻き上げられない vi.doMock と vi.doUnmock は従来どおりどこでも呼べます。

# インデントされた位置で呼んでいる vi.mock を一覧にする(トップレベル以外の候補)
grep -rnE "^\s+vi\.(mock|unmock|hoisted)\(" src tests

ヒットした箇所は、トップレベルへ移すか vi.doMock へ書き換えます。テストごとにモックの中身を変えたいなら、トップレベルで vi.mock し、各テストで vi.mocked(fn).mockReturnValue() を差し替える形がわかりやすいでしょう。あわせて、vi.fn(Class) や vi.spyOn で作ったクラスのモックが prototype のメソッドを持つようになりました。instanceof が true を返すため、4 で回避のために書いた型判定の分岐は不要になります。

非同期アサーションとsequential削除で失敗するテストの書き換え

resolvesとrejectsのawait漏れが警告から失敗に変わる影響範囲

4 では expect(promise).resolves.toBe(1) と await を書き忘れても、警告が出るだけでテストは通っていました。実際にはアサーションが評価される前にテストが終わっており、何も検証していない状態です。5 ではこれがテストの失敗になります。対象は resolves・rejects・toMatchFileSnapshot などの非同期アサーションです。

// 4 では警告のみで通過していた書き方(5 では失敗)
test('ユーザーを取得できる', () => {
  expect(fetchUser(1)).resolves.toEqual({ id: 1 })
})

// 5 で通る書き方
test('ユーザーを取得できる', async () => {
  await expect(fetchUser(1)).resolves.toEqual({ id: 1 })
})

この変更で落ちたテストは、await を足すと別の理由で失敗することがあります。これまで検証されていなかった期待値が初めて評価されるためです。CI が赤くなった件数の多さに驚く必要はありません。隠れていた不具合を拾えている状態です。expect.poll も同じ流れで、timeout を過ぎた時点で失敗するようになりました。遅れて条件を満たせば通っていたテストは、timeout を延ばすか待つ対象を見直します。

test.sequentialを{ concurrent: false }に直す例

test.sequential・describe.sequential・sequential オプションは削除されました。移行ガイドの該当項が示す置き換え先は、テストオプションの { concurrent: false } です。

// 4 までの書き方(5 では削除)
describe.sequential('決済フロー', () => { /* ... */ })

// 5 での書き方
describe('決済フロー', { concurrent: false }, () => { /* ... */ })

機械的に置換できるため、エディタの一括置換で済みます。もう1点、toThrow('') の意味が変わりました。空文字は部分一致として扱われ、どんなエラーメッセージにも一致します。「メッセージが空のエラー」を確かめたい意図で書いていた箇所は toThrow(/^$/) へ直します。-t によるテスト名の絞り込みも、math > adds のように > でつないだ完全名に対して照合する方式へ変わりました。CI の分割実行で空白区切りの名前を渡している場合は確認が要ります。

ブラウザモードのロケーター厳密一致とtoHaveTextContentの書き換え

getByTextの完全一致化でItem 1を拾わなくなる場合の直し方

5 のブラウザモードでは、ロケーターが既定で完全一致になりました。移行ガイドのロケーターの項のとおり大文字と小文字も区別され、getByText('Item') は「Item 1」に一致しなくなります。ラベルの一部だけを書いて要素を取っていたテストは、要素が見つからないエラーで落ちます。

直し方は、画面に出る文字列を正確に書くことです。部分一致のほうが意図に合う場面では、そのロケーターだけに渡す指定は { exact: false } です。全体を 4 の挙動へ戻す browser.locators.exact: false もありますが、文言変更でテストが通り続ける弱さまで戻ってしまいます。要素が見つからないときのエラーには ARIA スナップショットが添えられるようになったため、正しい文言は出力から拾えます。ブラウザモードの導入や headless 設定はVitest Browser Modeの導入とjsdomとの使い分けを参照してください。

toHaveTextContent完全一致化・toMatchTextContent移行

ブラウザモードの toHaveTextContent も完全一致に変わり、部分一致と正規表現を受け付けなくなりました。部分一致や正規表現で検証したい場合は、新しく入った toMatchTextContent を使います。

// 4 までは部分一致で通っていた
await expect.element(page.getByRole('status')).toHaveTextContent('保存しました')

// 5 で部分一致を残すなら toMatchTextContent へ
await expect.element(page.getByRole('status')).toMatchTextContent(/保存しました/)

どちらへ寄せるかは、文言を仕様として固定したいかで決めます。メッセージ全文が要件で決まっているなら toHaveTextContent のまま期待値を全文にし、日時や件数が混ざる表示なら toMatchTextContent にします。設定面では browser.api が廃止されトップレベルの api へ移りました。toMatchScreenshot の参照画像は browser.expect.toMatchScreenshot.screenshotDirectory で指定する形に変わっています。

CIの成果物とプロジェクト設定の変更|.vitest集約と設定の継承

JSONとJUnitの出力がstdoutから.vitest配下へ移るCI設定の修正

5 ではレポートと添付ファイルの出力先が、プロジェクトルートの .vitest ディレクトリにまとまりました。移行ガイドの出力先の項によると、JSON レポーターは .vitest/json/output.json、JUnit レポーターは .vitest/junit/output.xml、HTML レポーターは .vitest/index.html へ書き出されます。JSON と JUnit は 4 では標準出力に出ていたため、標準出力をリダイレクトして取り込む CI はファイルが空になります。

# GitHub Actions の例:JUnit の結果を 5 の出力先から拾う
- run: npx vitest run --reporter=junit
- uses: actions/upload-artifact@v4
  with:
    name: vitest-junit
    path: .vitest/junit/output.xml

標準出力のまま受け取りたいなら、設定で ['json', { stdout: true }] のように指定します。失敗時のスクリーンショットも .vitest/attachments/failure-screenshots/ へ移ったので、成果物のアップロードパスを合わせて変えます。.gitignore に必要な記述は .vitest の1行だけです。

インラインプロジェクトのextends既定trueでsetupFilesが重なる例

projects に直接書いたインラインプロジェクトは、5 ではルートの設定を既定で引き継ぎます。4 で必要だった extends: true の指定は不要です。注意が要るのは配列の扱いで、setupFiles などは上書きではなく追加になります。ルートとプロジェクトの両方に同じ setup ファイルを書いていた構成では、setup が2回走ります。

プロジェクト側から重複した記述を消すのが素直な直し方です。プロジェクトごとに完全に独立させたい場合は extends: false を付けます。インラインプロジェクトが Vite サーバーを1つ共有する sharedViteServer も既定で有効になり、プラグインの config フックは一度しか動きません。もう1点、VITEST_POOL_ID と VITEST_WORKER_ID が 1 始まりになりました。ワーカー ID からテスト用 DB 名やポート番号を計算している構成は、0 番を前提にした分岐がずれます。

ベンチマークAPIの再設計|benchをテストのフィクスチャで書く手順

ベンチマークを書いているプロジェクトだけが対象です。5 では bench がトップレベルで import する関数ではなくなり、ベンチマークファイル内の test() が受け取るフィクスチャになりました。モジュールスコープの bench(name, fn) と、bench.skip・bench.only・bench.todo は削除されています。

import { test } from 'vitest'

test('配列ソートの比較', async ({ bench }) => {
  await bench('Array.prototype.sort', () => {
    [3, 1, 2].sort()
  }).run()
})

設定の benchmark.reporters と benchmark.outputFile は test.reporters へ寄せ、ベースラインとの比較は writeResult で保存した結果を bench.from() で読み込む方式へ変わりました。CLI の --compare と --outputJson も廃止で、JSON が要るなら --reporter=json --outputFile=<path> を使います。CI で性能の退行を検知していた場合は、比較の仕組みごと組み直す前提で工数を見込みます。

受託開発でVitest 5へ上げる判断|今すぐ移る条件と4.1系に残す場面

5.0系へ上げてよい2条件とNode.js 20を抱える案件での据え置き

今すぐ上げてよいのは、次の2つを両方満たすプロジェクトです。CI と開発端末がすでに Node.js 22.12 以上で動いていること。テスト件数が数百本規模で、落ちたテストを数日で直し切れること。この場合は 5.0.3 で検証ブランチを作り、赤くなったテストを直してから本流へ戻すだけで済みます。await 漏れの失敗化で検証されていなかった期待値が見つかるため、上げる利得はテスト基盤の信頼性として返ってきます。

据え置くのは、Node.js 20 系の本番ランタイムに CI を合わせている案件です。テストだけ 22 系で回すと実行環境とテスト環境の差が生まれ、5 へ上げる利得を上回ります。ランタイムの更新計画が決まるまでは 4.1.11 に固定してください。ベンチマークで性能退行を検知している案件も、比較の仕組みを作り直す工数が確保できるまで見送りが妥当です。

保守契約の定例作業計画にテスト基盤の版上げと検証を組み込む進め方

Vitest は 4.0 が 2025年10月、5.0 が 2026年9月と、ほぼ1年おきにメジャー版を出しています。そのたびに既定値の変更でテストが落ちるため、版上げは障害対応ではなく定例作業として扱うほうが総工数を抑えられます。手順は、npm の dist-tag で新メジャーの公開を検知する、検証ブランチで全件を走らせる、落ちた件数と原因の内訳で工数を見積もる、の順です。

受託で納品したシステムでは、テスト基盤の版上げが保守範囲に入っているかが曖昧なまま放置されがちです。依存の更新とテストの修正を保守契約の作業項目に含めておくと、Node.js のサポート終了と重なった時期にまとめて慌てずに済みます。運用中のシステムで依存更新の体制から整えたい場合は、保守運用と内製化支援のサービスで、更新手順の設計と社内への引き継ぎまで相談できます。

よくある質問

Vitest 4 から 5 への移行について、実装と運用の現場から確認されることの多い5点です。

Vitest 5はいつリリースされましたか?

Vitest 5.0.0 は 2026年9月3日に npm へ公開され、同日に公式ブログで告知されました。その後 9月15日に 5.0.1、9月25日に 5.0.2、9月30日に 5.0.3 が出ており、2026年10月12日時点の最新は 5.0.3 です。4 系の最終は dist-tag V4 が指す 4.1.11(2026年8月18日公開)で、npm install -D vitest@4 のように版を指定すれば引き続き入れられます。

Vitest 4と5の違いは何ですか?

大きな違いは既定値の厳格化です。clearMocks が既定で有効になり、await のない非同期アサーションは失敗扱いになりました。ブラウザモードのロケーターと toHaveTextContent は完全一致へ変わっています。動作要件は Node.js 22.12.0 以上と Vite 6.4.0 以上へ上がりました。機能面では引数ごとに戻り値を決める vi.when、実行を再生できる Trace View、設定の改善点を示す vitest doctor、不安定なテストを洗い出す --repeats が加わっています。

Node.js 20のままVitest 5を使えますか?

使えません。5.0.3 の engines.node は ^22.12.0 || ^24.0.0 || >=26.0.0 で、Node.js 20 系は対象外です。公式の移行ガイドも、古い環境では予期しないエラーが起きる可能性があるため他の作業より先に確認するよう求めています。ランタイムを上げられない事情があるなら 4.1.11 に固定し、Node.js の更新と同じ時期に Vitest 5 への移行を組み込むのが現実的です。

Vitest 5に上げたらテストが大量に失敗したのはなぜですか?

多くは await を書き忘れた resolves や rejects が失敗扱いになったことと、clearMocks の既定化で呼び出し回数のアサーションが崩れたことが原因です。前者は 4 では警告だけで通過し、実際には何も検証していませんでした。await を足すと期待値が初めて評価され、別の理由で落ちるテストも出てきます。エラーメッセージで原因を分類し、件数の多い種類から直すと短時間で収束します。

Vitest 5で削除されたAPIはどれですか?

テストコードでは test.sequential・describe.sequential・sequential オプションが削除され、{ concurrent: false } に置き換わりました。モジュールスコープの bench と bench.skip などもなくなり、ベンチマークはテストのフィクスチャで書きます。設定では browser.api がトップレベルの api へ移りました。エントリポイントの vitest/coverage と vitest/reporters は vitest/node へ、vitest/environments は vitest/runtime へ統合されています。

関連記事

お気に入りに入れた記事の一覧

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.09 テックブログ IDCFクラウド(IDCフロンティア)不正アクセス・ランサムウェア:影響先・復旧・データは戻るか
  2. 2026.10.09 テックブログ ニッスイのサイバー攻撃で日水物流の入出荷停止|委託先クラウド障害に荷主が備える手順
  3. 2026.07.15 コラム LINEミニアプリとは?未認証と認証済みの違い・LIFFとの使い分け・審査と開発費用【2026年版】
  4. 2026.07.23 コラム クラウド販売管理システムとは?オンプレとの違い・料金相場と5年総額の比べ方【2026年】
  5. 2026.10.09 テックブログ 京王電鉄のランサムウェア被害とグループ共通基盤:決済・ポイント・予約が止まった範囲と遮断の初動

RELATED POSTS 関連記事

目次