はじめに
Vitestを基礎の基礎から学んでいく過程をまとめてみます。
筆者は、Jest, Mocha, Jasmineなども触れたことがあり、実際にVitestでテストコードを書いているけど、いまいち理解が乏しいためか実行時エラーの解決に時間がかかる、というレベル感です。
Vitestとは
Vitest is a next generation testing framework powered by Vite.
(Vitestは、Viteを基盤とする次世代テストフレームワークです。)Getting Started より引用
テストフレームワークとは?
テストを実行・確認するための仕組みをまとめたもので、次のような要素があります。
- テストランナー(テストを読み込んで実行する)
- テスト構文(テストを書くために用意されているAPIや記法)
- アサーション(テスト結果を判定するために用意されているAPIや記法)
- モック/スパイ(外部依存を置き換えたり、呼び出し状況を記録する)
- レポート出力(テストの成功・失敗をまとめて表示したり、CIに出力できる)
例:Vitest の場合
Vitest は上記の要素をすべて含んでいます。
- テストランナー:Viteの仕組みで超高速にテストを実行
- テスト構文:
describe,it,test,bench - アサーション:Jest互換の
expect - モック/スパイ:
vi.fn,vi.spyOn,vi.mock - レポート:CLI上で見やすい結果を出す
なぜVitestを導入するの?他のテストフレームワークより優れてるの?
Vitestは「Jestを高速・シンプルにしたモダン版」という立ち位置。
Jestと互換性があり移行しやすいことが公式サイトでも特徴としても挙げられています。また、既にViteを使っている場合は、設定ファイルの共有や実行速度の速さというメリットがあるためVitestを選ばれることが多いようです。
[参考] 他のテストフレームワークについて
Mocha:テストランナー中心でアサーションやモック/スパイなどは外部ライブラリを組み合わせる必要がある
Jasmine:全部入りに近いが、JestやVitestと比べてできることが少なくシンプルな作り
VitestはJestは似てるの?違いは?
Vitestは Jestとほとんど同じ書き方でテストできるので、Jest経験者はすぐに使えます。
ただし、中で動いている仕組みが違うため「速さ」や「設定の手軽さ」に差があります。
👬似ているところ
- テストの書き方
-
describe/it/testでテストを書く -
expectで結果を判定する - モック(
vi.fn,vi.spyOn,vi.mock)の使い方もほぼ同じ
-
- スナップショットやカバレッジも対応
🙅♀️違うところ
- 動かす仕組み(エンジン)
- Jest → 独自の実行環境
- Vitest → Vite+esbuild を利用
- メリット
- ファイルを修正すると、その部分だけを素早くテスト(ウォッチが速い)
- TypeScript や JSX も追加ツールなしでそのまま動く
テストを整理する仕組み
テストは無秩序にかかず、グループ化(describe / it / test)と準備・後片付け(フック) を使って整理します。
フックの仕組みは Vitest に限らず、多くのJavaScriptテストフレームワークに共通しています。
describe と it/test
describe
- テストをまとめる「箱」= テストスイートと呼ばれるもの
- 関連するテストケースをグループ化して見やすくする
- ネスト(入れ子)にして整理することも可能
it / test
- 実際のテストを定義する関数= テストケースと呼ばれるもの
-
itとtestはどちらも使っても同じ意味・結果で、好みに応じて使い分ける-
it→ 英語の文章っぽく自然に読めるスタイルにしたいときに選ばれる -
test→ シンプルに淡々と「テスト」として書きたいときに選ばれる
-
フックの種類と役割
テストを書くとき、毎回同じ準備や後片付けをしたい場面があります。その時に使うのが フック(hook)です。
例えば、あるテストで追加したデータが次のテストに残ってしまうと結果が変わってしまいますが、フックを使うことで毎回リセットや初期化を行うことができます。
Vitest(JestやMochaも同じ)の代表的なフック
| フック | タイミング | 主な用途 |
|---|---|---|
| beforeAll | スイートの最初の1回 | 重い初期化(DB接続、モックサーバー起動など) |
| afterAll | スイートの最後の1回 | 重い後片付け(DB切断、モックサーバー停止など) |
| beforeEach | 各テストの直前 | 軽い初期化(変数リセット、モックリセットなど) |
| afterEach | 各テストの直後 | 軽い後片付け(状態クリア、モック検証など) |
テストの基本動作
テストは「登録」してから「実行」される、という2段階で動きます。
これはVitest特有の仕組みではなく、一般的なテストフレームワークすべてに共通しています。
登録と実行の流れ
1. テストの登録(ファイルを読み込む時)
- どんなテストがあるかリストアップして登録する
- 登録とは「フレームワーク内部のテストランナーにテスト情報を格納する」こと
- 登録の例:
-
describe→ テストスイート(グループ)として登録 -
it/test→ 個々のテストケースとして登録
-
- 実行は登録が終わったあとにまとめて行われる
2. 実行フェーズ(登録が終わった後)
- 登録されたテストケースを1つずつ実行する
-
expectで判定したり、モックを確認したりするのはここで行われる
[参考] describeは非同期にできない
登録フェーズは「実行すべきテストの一覧」を作る段階なので同期的にすぐ完了する必要があります。
そのため、describeを非同期にすると、どのテストを登録すればいいかわからなくなってしまいます。
// ❌ NGな例
describe('async suite', async () => {
const data = await fetchData() // テストの登録が止まってしまう!
it('test with data', () => {
expect(data).toBe('ok')
})
})
非同期処理をしたい場合は?
1. テストケース内で async/await を使う
登録時に個々のテストケースは実行されないため、テストケース内であれば非同期処理が書けます。
// ✅ OKな例
it('fetches data directly in the test', async () => {
const data = await fetchData()
expect(data).toBe('ok')
})
2. フック(beforeAll / beforeEach)で事前にデータを準備する
テスト実行前に必要なデータを取得するやり方でも非同期処理が書けます。
// ✅ OKな例
describe('with async data', () => {
let data: string
beforeAll(async () => {
data = await fetchData()
})
it('uses the prepared data', () => {
expect(data).toBe('ok')
})
})
