
Vitest
- 56 installs
- 2 repo stars
- Updated August 3, 2026
- fandhe-ai/agent-reference-skills
Helps with testing & qa tasks.
About
vitest is a Claude Code skill for testing & qa. It helps solo builders move faster with AI-assisted coding.
- vitest
- Testing & QA
- AI-coding skill
Vitest by the numbers
- 56 all-time installs (skills.sh)
- Ranked #1,185 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/fandhe-ai/agent-reference-skills --skill vitestAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 56 |
|---|---|
| repo stars | ★ 2 |
| Last updated | August 3, 2026 |
| Repository | fandhe-ai/agent-reference-skills ↗ |
What it does
Helps with testing & qa tasks.
Files
Vitest API リファレンス
Vitest — Vite ネイティブのテストフレームワーク。 テスト作成・レビュー・リファクタリング時に参照する。
ディレクトリ構成
skills/vitest/
SKILL.md
references/
api/
README.md
test-api.md
expect.md
vi.md
guide/
README.md
cli.md
config.md
coverage.md
environment.md
snapshot.md
testing-types.md
workspace.md
patterns/
README.md
mocking.md
async.md
samples/
README.md
basic-test.md
coverage-setup.md
describe-and-hooks.md
mocking-modules.md
parameterized-tests.md
snapshot-testing.md
spy-on-methods.md
test-environment.md
type-testing.md
workspace-setup.md
scripts/
README.md
cli.md
coverage.md
install.md
setup.md探索手順
タスクからカテゴリを引き、カテゴリの README.md で目的のページを特定する:
1. 下記マッピング表でタスクに対応するカテゴリを探す 2. そのカテゴリの references/{category}/README.md を参照して目的のページを特定する 3. 該当ページの .md を Read して詳細を確認する
タスク → カテゴリ マッピング
| タスク | カテゴリ | 参照 README |
|---|---|---|
| describe, it/test, hooks, modifiers, each/for を使いたい | api | references/api/README.md |
| expect のマッチャー(等値・型・数値・コレクション・スナップショット・非同期)を調べたい | api | references/api/README.md |
| vi.fn, vi.mock, vi.spyOn, タイマー, スタブを使いたい | api | references/api/README.md |
| vitest.config.ts の設定オプションを調べたい | guide | references/guide/README.md |
| CLI コマンド・フラグを調べたい(リファレンス) | guide | references/guide/README.md |
| カバレッジ (v8 / istanbul) を設定したい | guide | references/guide/README.md |
| テスト環境 (jsdom, happy-dom, edge-runtime) を切り替えたい | guide | references/guide/README.md |
| スナップショットテスト・型テスト・ワークスペースを設定したい | guide | references/guide/README.md |
| モックパターン (vi.fn, vi.mock, vi.spyOn, 部分モック) を知りたい | patterns | references/patterns/README.md |
| 非同期テスト・フェイクタイマーのパターンを知りたい | patterns | references/patterns/README.md |
| 典型的な使い方・実例を参照したい | samples | samples/README.md |
| インストール・CLI コマンド・セットアップ手順を知りたい | scripts | scripts/README.md |
Expect マッチャー
expect(value) でアサーションラッパーを作成し、マッチャーをチェーンする。 .not でマッチャーを否定できる。
等値マッチャー
| Matcher | Description |
|---|---|
toBe(value) | Object.is による厳密等値。プリミティブ・参照比較向き |
toEqual(value) | 再帰的な構造等値。undefined プロパティは無視 |
toStrictEqual(value) | toEqual + undefined プロパティ・配列の疎密・オブジェクト型も検査 |
toMatchObject(subset) | オブジェクトが指定サブセットのプロパティを持つか検査 |
expect(1 + 1).toBe(2)
expect({ a: 1, b: 2 }).toEqual({ a: 1, b: 2 })
expect({ a: 1, b: 2 }).toMatchObject({ a: 1 })型・値マッチャー
| Matcher | Description |
|---|---|
toBeTruthy() | truthy な値 |
toBeFalsy() | falsy な値 |
toBeNull() | null |
toBeUndefined() | undefined |
toBeDefined() | undefined でない |
toBeNaN() | NaN |
toBeTypeOf(type) | typeof value === type(`'string' \ |
toBeInstanceOf(Class) | 指定クラスのインスタンス |
数値マッチャー
| Matcher | Description |
|---|---|
toBeGreaterThan(n) | value > n |
toBeGreaterThanOrEqual(n) | value >= n |
toBeLessThan(n) | value < n |
toBeLessThanOrEqual(n) | value <= n |
toBeCloseTo(n, precision?) | 浮動小数点の近似比較(デフォルト精度 2 桁) |
コレクションマッチャー
| Matcher | Description |
|---|---|
toContain(item) | 配列にアイテムを含む / 文字列に部分文字列を含む |
toContainEqual(item) | 配列に構造的に等しいアイテムを含む(toEqual ロジック) |
toHaveLength(n) | .length === n |
toHaveProperty(keyPath, value?) | プロパティの存在確認(ドット記法・配列パス対応) |
expect([1, 2, 3]).toContain(2)
expect([{ id: 1 }, { id: 2 }]).toContainEqual({ id: 1 })
expect({ user: { name: 'Alice' } }).toHaveProperty('user.name', 'Alice')文字列マッチャー
| Matcher | Description |
|---|---|
toMatch(pattern) | 正規表現または部分文字列にマッチ |
expect('hello world').toMatch(/world/)
expect('hello world').toMatch('world')エラーマッチャー
| Matcher | Description |
|---|---|
toThrow(error?) | 関数が例外をスローする(メッセージ/クラスで検証可能) |
toThrowError(error?) | toThrow のエイリアス |
expect(() => JSON.parse('{')).toThrow(SyntaxError)
expect(() => fn()).toThrow('expected message')
expect(() => fn()).toThrow(/pattern/)スナップショットマッチャー
| Matcher | Description |
|---|---|
toMatchSnapshot(hint?) | 保存済みスナップショットと比較。初回は作成 |
toMatchInlineSnapshot(snapshot?) | テストファイル内にインラインでスナップショット保存 |
toMatchFileSnapshot(filepath) | 指定ファイルとスナップショット比較(async) |
toThrowErrorMatchingSnapshot(hint?) | toThrow + toMatchSnapshot |
toThrowErrorMatchingInlineSnapshot(snapshot?) | toThrow + toMatchInlineSnapshot |
expect({ a: 1 }).toMatchSnapshot()
expect({ a: 1 }).toMatchInlineSnapshot(`
{
"a": 1,
}
`)モック/スパイマッチャー
| Matcher | Description |
|---|---|
toHaveBeenCalled() | 1回以上呼ばれた |
toHaveBeenCalledTimes(n) | ちょうど n 回呼ばれた |
toHaveBeenCalledWith(...args) | 指定引数で呼ばれた(任意の呼び出し) |
toHaveBeenCalledExactlyOnceWith(...args) | 1回だけ指定引数で呼ばれた |
toHaveBeenLastCalledWith(...args) | 最後の呼び出しが指定引数 |
toHaveBeenNthCalledWith(n, ...args) | n 番目の呼び出しが指定引数(1始まり) |
toHaveReturned() | 1回以上正常にリターンした |
toHaveReturnedTimes(n) | ちょうど n 回リターンした |
toHaveReturnedWith(value) | 指定値をリターンした |
toHaveLastReturnedWith(value) | 最後のリターン値が一致 |
toHaveNthReturnedWith(n, value) | n 番目のリターン値が一致 |
const fn = vi.fn(() => 42)
fn('hello')
expect(fn).toHaveBeenCalledWith('hello')
expect(fn).toHaveReturnedWith(42)非同期マッチャー
| Modifier | Description |
|---|---|
resolves | Promise の解決値をアンラップ(await 必須) |
rejects | Promise の拒否理由をアンラップ(await 必須) |
await expect(Promise.resolve(42)).resolves.toBe(42)
await expect(Promise.reject(new Error('fail'))).rejects.toThrow('fail')アサーション制御
| Method | Description |
|---|---|
expect.assertions(n) | テスト内でちょうど n 個のアサーションが実行されることを検証 |
expect.hasAssertions() | 少なくとも 1 つのアサーションが実行されることを検証 |
expect.unreachable(msg?) | 到達すべきでないコードパス(到達時に失敗) |
test('async callback is called', async () => {
expect.assertions(1)
const data = await fetchData()
expect(data).toBeDefined()
})ソフトアサーション
expect.soft(a).toBe(1) // 失敗してもテスト続行
expect.soft(b).toBe(2) // 全失敗をまとめて報告poll(リトライアサーション)
await expect.poll(() => fetchStatus()).toBe('ready')
// デフォルト: interval 50ms, timeout 1000ms
await expect.poll(() => count, { interval: 100, timeout: 5000 }).toBeGreaterThan(10)非対称マッチャー
toEqual / toMatchObject 内で使用可能。
| Matcher | Description |
|---|---|
expect.anything() | null / undefined 以外の任意の値 |
expect.any(Class) | 指定クラスのインスタンス |
expect.arrayContaining(items) | 指定アイテムを全て含む配列 |
expect.objectContaining(obj) | 指定プロパティを含むオブジェクト |
expect.stringContaining(str) | 部分文字列を含む文字列 |
expect.stringMatching(pattern) | 正規表現にマッチする文字列 |
expect.closeTo(n, precision?) | 浮動小数点の近似マッチ |
expect({ id: 1, name: 'Alice', createdAt: new Date() }).toEqual({
id: expect.any(Number),
name: expect.stringContaining('Ali'),
createdAt: expect.any(Date),
})
expect([1, 2, 3, 4]).toEqual(expect.arrayContaining([1, 3]))関連
- Test API
- Vi ユーティリティ
- モックパターン
api
| Name | Description | Path |
|---|---|---|
| Expect マッチャー | expect(value) でアサーションラッパーを作成し、マッチャーをチェーンする。 | expect.md |
| Test API | test-api.md | |
| Vi ユーティリティ | vi オブジェクトはモック、スパイ、タイマー、スタブの操作を提供する。 | vi.md |
Test API
describe
テストスイートを定義する。ネスト可能。
describe(name: string, fn: () => void, timeout?: number): voiddescribe('math utils', () => {
it('adds numbers', () => {
expect(1 + 1).toBe(2)
})
describe('edge cases', () => {
it('handles zero', () => {
expect(0 + 0).toBe(0)
})
})
})describe のモディファイア
| Modifier | Description |
|---|---|
describe.only | このスイートのみ実行 |
describe.skip | スキップ |
describe.todo | 未実装マーク |
describe.concurrent | 内部テストを並列実行 |
describe.sequential | concurrent コンテキスト内で順次実行を強制 |
describe.shuffle | ランダム順で実行 |
describe.each(table) | テーブル駆動でスイートを繰り返し |
describe.each([
{ input: 1, expected: 2 },
{ input: 2, expected: 4 },
])('double($input)', ({ input, expected }) => {
it(`returns ${expected}`, () => {
expect(input * 2).toBe(expected)
})
})test / it
個別のテストケースを定義する。it は test のエイリアス。
test(name: string, fn?: () => void | Promise<void>, timeout?: number): voidtest('returns correct value', () => {
expect(sum(1, 2)).toBe(3)
})
test('async test', async () => {
const data = await fetchData()
expect(data).toBeDefined()
})test のオプション
| Option | Type | Default | Description |
|---|---|---|---|
timeout | number | 5000 | タイムアウト(ms) |
retry | number | 0 | 失敗時のリトライ回数 |
repeats | number | 0 | テストの繰り返し回数 |
concurrent | boolean | false | 並列実行 |
sequential | boolean | true | 順次実行 |
tags | string[] | [] | テストタグ |
test のモディファイア
| Modifier | Description |
|---|---|
test.only | このテストのみ実行(CI では使用禁止エラー) |
test.skip | スキップ |
test.todo | 未実装マーク(body 不要) |
test.fails | テストが失敗することを期待 |
test.concurrent | 並列実行 |
test.sequential | concurrent スイート内で順次実行 |
test.skipIf(condition) | 条件が truthy ならスキップ |
test.runIf(condition) | 条件が truthy の場合のみ実行 |
test.override(v4.1.0+)
test.extend で定義したフィクスチャをスイート内でスコープ付きオーバーライドする。
const myTest = test.extend<{ port: number }>({
port: 3000,
})
myTest.override({ port: 8080 })('uses port 8080', ({ port }) => {
expect(port).toBe(8080)
})test.each(テーブル駆動テスト)
配列またはテンプレートリテラルを受け取る。
test.each([
[1, 1, 2],
[2, 3, 5],
[0, 0, 0],
])('add(%i, %i) = %i', (a, b, expected) => {
expect(a + b).toBe(expected)
})フォーマット指定子: %s(文字列), %d(数値), %i(整数), %f(浮動小数点), %j(JSON), %#(インデックス), %$(テスト番号)
test.for
test.each の代替。配列引数をスプレッドせず、TestContext にアクセス可能。
test.for([
{ a: 1, b: 1, expected: 2 },
{ a: 2, b: 3, expected: 5 },
])('add($a, $b) = $expected', ({ a, b, expected }, { expect }) => {
expect(a + b).toBe(expected)
})test.extend(フィクスチャ)
テストコンテキストにカスタムフィクスチャを追加する。
const myTest = test.extend<{ db: Database }>({
db: async ({}, use) => {
const db = await createTestDb()
await use(db)
await db.cleanup()
},
})
myTest('uses fixture', ({ db }) => {
expect(db).toBeDefined()
})ライフサイクルフック
beforeAll(fn: () => void | Promise<void>, timeout?: number): void
afterAll(fn: () => void | Promise<void>, timeout?: number): void
beforeEach(fn: () => void | Promise<void>, timeout?: number): void
afterEach(fn: () => void | Promise<void>, timeout?: number): voiddescribe('database tests', () => {
beforeAll(async () => {
await db.connect()
})
afterAll(async () => {
await db.disconnect()
})
beforeEach(() => {
vi.clearAllMocks()
})
it('queries data', async () => {
const result = await db.query('SELECT 1')
expect(result).toBeDefined()
})
})フックの実行順序
beforeEach: 外側 → 内側afterEach: 内側 → 外側- トップレベル(
describe外)のフックはファイル内の全テストに適用
context.skip()(動的スキップ)
テスト実行中に条件に応じてスキップできる。
test('dynamic skip', ({ skip }) => {
if (someCondition) skip()
// 以降は実行されない
})context.annotate()(テストアノテーション)
テスト実行中に注釈を追加する(v4.0+)。
test('annotated', async ({ annotate }) => {
await annotate('notice message')
await annotate('error detail', 'error')
})型: 'notice' | 'warning' | 'error'(省略時は 'notice')
関連
- Expect マッチャー
- Vi ユーティリティ
Vi ユーティリティ
vi オブジェクトはモック、スパイ、タイマー、スタブの操作を提供する。
関数モック
vi.fn()
呼び出し追跡付きのモック関数を作成する。
vi.fn<T extends Procedure>(implementation?: T): MockInstance<T>const mockFn = vi.fn()
mockFn('hello')
expect(mockFn).toHaveBeenCalledWith('hello')
const greet = vi.fn((name: string) => `Hello, ${name}`)
expect(greet('Alice')).toBe('Hello, Alice')モックインスタンスのメソッド
| Method | Description |
|---|---|
mockReturnValue(val) | 毎回 val を返す |
mockReturnValueOnce(val) | 次の呼び出しのみ val を返す |
mockResolvedValue(val) | Promise.resolve(val) を返す |
mockResolvedValueOnce(val) | 次の呼び出しのみ resolved promise を返す |
mockRejectedValue(err) | Promise.reject(err) を返す |
mockRejectedValueOnce(err) | 次の呼び出しのみ rejected promise を返す |
mockImplementation(fn) | 実装を差し替え |
mockImplementationOnce(fn) | 次の呼び出しのみ差し替え |
mockClear() | 呼び出し履歴をリセット(実装は保持) |
mockReset() | 履歴 + 実装をリセット(undefined を返す) |
mockRestore() | 元の実装を復元(vi.spyOn のみ有効) |
const fn = vi.fn()
.mockReturnValueOnce('first')
.mockReturnValue('default')
fn() // 'first'
fn() // 'default'呼び出し情報
fn.mock.calls // [[arg1, arg2], [arg1], ...]
fn.mock.results // [{ type: 'return', value: ... }, ...]
fn.mock.instances // new 呼び出し時のインスタンス
fn.mock.lastCall // 最後の呼び出しの引数vi.isMockFunction()
値がモック関数かどうかを判定する型ガード。
vi.mockObject()(v3.2.0+)
オブジェクトのメソッドを再帰的にモックする。vi.mock() のオブジェクト版。
function mockObject<T>(value: T, options?: { spy?: boolean }): MaybeMockedDeep<T>const original = {
simple: () => 'value',
nested: { method: () => 'real' },
}
// 全メソッドを vi.fn() に置換
const mocked = vi.mockObject(original)
expect(mocked.simple()).toBe(undefined)
mocked.simple.mockReturnValue('mocked')
// 元の実装を保持してスパイ
const spied = vi.mockObject(original, { spy: true })
expect(spied.simple()).toBe('value')
expect(spied.simple).toHaveBeenCalled()モジュールモック
vi.mock()
モジュール全体をモックに置換する。ファイル先頭にホイストされる。
vi.mock(modulePath: string, factory?: () => unknown): void// オートモック(全エクスポートが vi.fn() になる)
vi.mock('./utils')
// ファクトリモック
vi.mock('./api', () => ({
fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Alice' }),
}))
// 部分モック
vi.mock('./utils', async (importOriginal) => {
const actual = await importOriginal<typeof import('./utils')>()
return {
...actual,
dangerousOp: vi.fn(),
}
})vi.doMock()
ホイストされない vi.mock。次の動的 import に適用される。
vi.unmock() / vi.doUnmock()
モックレジストリからモジュールを除去し、元のモジュールを復元する。
vi.importActual()
モックをバイパスして元のモジュールをインポートする。
vi.mock('./config', async (importOriginal) => {
const actual = await importOriginal<typeof import('./config')>()
return { ...actual, debug: true }
})vi.importMock()
オートモック版のモジュールをインポートする。
vi.resetModules()
モジュールキャッシュをクリアし、再インポート時に再評価させる。
オブジェクトスパイ
vi.spyOn()
既存オブジェクトのメソッドにスパイを設定する。元の実装はデフォルトで保持。
vi.spyOn<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set'): MockInstanceconst spy = vi.spyOn(console, 'warn')
callFn()
expect(spy).toHaveBeenCalledWith(expect.stringContaining('deprecated'))
spy.mockRestore()
// getter/setter
vi.spyOn(obj, 'value', 'get').mockReturnValue(100)モック管理
| Method | Description |
|---|---|
vi.clearAllMocks() | 全スパイの .mockClear() を呼ぶ |
vi.resetAllMocks() | 全スパイの .mockReset() を呼ぶ |
vi.restoreAllMocks() | 全スパイの元の実装を復元 |
環境・グローバルスタブ
vi.stubEnv()
環境変数を一時的に変更する。
vi.stubEnv('NODE_ENV', 'production')
vi.stubEnv('API_URL', 'https://test.example.com')
vi.unstubAllEnvs() // 全復元vi.stubGlobal()
グローバル変数を一時的に変更する。
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ json: () => ({}) }))
vi.unstubAllGlobals() // 全復元フェイクタイマー
vi.useFakeTimers()
setTimeout, setInterval, Date 等をフェイクに置換する。
vi.useFakeTimers(config?: FakeTimerInstallOpts): void| Method | Description |
|---|---|
vi.useFakeTimers() | フェイクタイマーを有効化 |
vi.useRealTimers() | リアルタイマーに復元 |
vi.advanceTimersByTime(ms) | 指定ミリ秒分タイマーを進める |
vi.advanceTimersByTimeAsync(ms) | 非同期版 |
vi.advanceTimersToNextTimer() | 次のタイマーまで進める |
vi.advanceTimersToNextTimerAsync() | 非同期版 |
vi.advanceTimersToNextFrame() | requestAnimationFrame コールバックを進める |
vi.runAllTimers() | 全タイマーを実行(無限ループ防止: 10,000 回制限) |
vi.runAllTimersAsync() | 非同期版 |
vi.runOnlyPendingTimers() | 現在キューにあるタイマーのみ実行 |
vi.runOnlyPendingTimersAsync() | 非同期版 |
vi.setSystemTime(date) | Date.now() / new Date() を固定値に設定 |
vi.getRealSystemTime() | 実際のシステム時刻を取得 |
vi.getMockedSystemTime() | モック中の Date を取得(未モック時は null) |
vi.getTimerCount() | キュー内のタイマー数 |
vi.clearAllTimers() | 全タイマーを実行せずに削除 |
vi.isFakeTimers() | フェイクタイマーが有効かどうか |
vi.setTimerTickMode(mode) | タイマーの進行モードを設定(v4.1.0+) |
vi.setTimerTickMode()(v4.1.0+)
フェイクタイマーのティックモードを設定する。
| Mode | Description |
|---|---|
'manual' | vi.advanceTimersByTime() 等で手動制御(デフォルト) |
'nextTimerAsync' | 非同期タイマーを自動的に次のタイマーまで進める |
'interval' | 指定間隔ごとに自動でタイマーを進める |
vi.useFakeTimers()
vi.setTimerTickMode('nextTimerAsync')beforeEach(() => { vi.useFakeTimers() })
afterEach(() => { vi.useRealTimers() })
it('fires after delay', () => {
const fn = vi.fn()
setTimeout(fn, 1000)
vi.advanceTimersByTime(1000)
expect(fn).toHaveBeenCalledOnce()
})
it('mocks current date', () => {
vi.setSystemTime(new Date('2024-01-01'))
expect(new Date().getFullYear()).toBe(2024)
})ユーティリティ
vi.hoisted()
vi.mock ファクトリ内で外部変数を参照するためのホイスト機構。
const mockFetch = vi.hoisted(() => vi.fn())
vi.mock('./api', () => ({ fetchData: mockFetch }))
mockFetch.mockResolvedValue({ data: [] })vi.waitFor()
コールバックが成功するまでリトライする。
await vi.waitFor(() => {
expect(element).toBeVisible()
}, { timeout: 5000, interval: 100 })vi.waitUntil()
コールバックが truthy を返すまで待機する。
const result = await vi.waitUntil(() => fetchStatus() === 'ready')vi.mocked()
TypeScript 用のモック型ヘルパー。
vi.mocked(myFn) // MockInstance<typeof myFn> として型推論
vi.mocked(obj, { deep: true }) // ディープモック型vi.dynamicImportSettled()
全ての動的 import の完了を待つ。
vi.setConfig()
テストファイル単位で Vitest の設定を上書きする。
vi.setConfig({ testTimeout: 10000, fakeTimers: { now: new Date(2024, 0, 1) } })vi.resetConfig()
vi.setConfig() で変更した設定を元に戻す。
afterAll(() => {
vi.resetConfig()
})vi.defineHelper()(v4.1.0+)
カスタムアサーションヘルパーを定義する。エラーのスタックトレースを呼び出し元に整形する。
const assertPositive = vi.defineHelper((value: number) => {
expect(value).toBeGreaterThan(0)
})
test('is positive', () => {
assertPositive(42) // エラー時のスタックがこの行を指す
})関連
- Test API
- Expect マッチャー
- モックパターン
- 非同期テストパターン
CLI コマンド
コマンド
| Command | Description |
|---|---|
vitest | デフォルト: dev では watch モード、CI では run モード |
vitest run | ウォッチなしで単一実行 |
vitest watch | ウォッチモードで実行(エイリアス: vitest dev) |
vitest bench | ベンチマークテストのみ実行 |
vitest related <files> | 指定ソースファイルに関連するテストのみ実行 |
vitest list | マッチするテスト一覧を出力 |
vitest init <name> | プロジェクト設定のセットアップ |
vitest typecheck | 型テストの実行 |
ファイル名で絞り込み可能: vitest foobar 行番号指定(v3+): vitest basic/foo.test.ts:10
主要フラグ
| Flag | Description |
|---|---|
--run | ウォッチモードを無効化 |
--reporter <name> | レポーター指定(default, verbose, dot, json, junit, tap, tree, blob, github-actions, minimal 等) |
--coverage.enabled | カバレッジ収集を有効化 |
--ui | UI を有効化 |
-u / --update | スナップショットを更新 |
--changed | 変更されたファイルに関連するテストのみ実行 |
--bail <n> | n 個のテスト失敗で実行を停止 |
--passWithNoTests | テストなしでも成功終了 |
--globals | API をグローバルに注入 |
--environment <name> | 実行環境を指定(デフォルト: node) |
-t / --testNamePattern <pattern> | パターンにマッチするテストのみ実行 |
-w / --watch | ウォッチモードを有効化 |
--project <name> | 特定プロジェクトのみ実行(複数指定可・ワイルドカード対応、!pattern で除外) |
--shard <index>/<count> | テストスイートをシャード分割(例: --shard=1/3) |
--tagsFilter <expr> | タグでテストを絞り込み(&&, `\ |
--listTags | 利用可能なタグ一覧を表示 |
--strictTags | 未定義タグをエラーとして扱う |
--browser.enabled | ブラウザモードを有効化 |
よくある使用例
# 単一実行(CI 向け)
vitest run
# カバレッジ付き実行
vitest run --coverage.enabled
# スナップショット更新
vitest run -u
# 特定テストのみ
vitest run -t "should handle errors"
# 変更ファイルに関連するテストのみ
vitest run --changed
# lint-staged 連携
vitest related src/utils.ts --run
# 特定プロジェクトのみ実行
vitest run --project unit --project e2e
# シャード分割(CI 並列化)
vitest run --shard=1/3
# タグでフィルタ
vitest run --tagsFilter "unit && !slow"ウォッチモードのキーボードショートカット
| Key | Action |
|---|---|
a | 全テストを再実行 |
f | 失敗テストのみ再実行 |
u | スナップショットを更新 |
p | ファイル名でフィルタ |
t | テスト名でフィルタ |
q | 終了 |
関連
- 設定
- カバレッジ
- スナップショット
設定 (vitest.config.ts)
vitest.config.ts(または vite.config.ts の test キー)で設定する。
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{ts,tsx}'],
setupFiles: ['./src/setup-tests.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
},
},
})コアオプション
| Option | Type | Default | Description |
|---|---|---|---|
globals | boolean | false | describe, it, expect 等をグローバル公開(import 不要) |
environment | string | 'node' | テスト環境(node, jsdom, happy-dom, edge-runtime) |
include | string[] | ['**/*.{test,spec}.{js,ts,jsx,tsx}'] | テストファイルの glob パターン |
exclude | string[] | ['**/node_modules/**', '**/dist/**', ...] | 除外パターン |
setupFiles | `string \ | string[]` | [] |
globalSetup | `string \ | string[]` | [] |
pool | string | 'forks' | ワーカープール(threads, forks, vmThreads, vmForks) |
testTimeout | number | 5000 | テストのデフォルトタイムアウト(ms) |
hookTimeout | number | 10000 | フックのデフォルトタイムアウト(ms) |
retry | number | 0 | 失敗テストのリトライ回数 |
reporters | string[] | ['default'] | レポーター(verbose, dot, json, html, junit, tap, tree, blob, github-actions, minimal 等) |
watch | boolean | dev: true | ウォッチモード |
passWithNoTests | boolean | false | テストファイルなしでも成功終了 |
typecheck | object | — | 型テスト設定 |
カバレッジオプション (test.coverage)
| Option | Type | Default | Description |
|---|---|---|---|
provider | `'v8' \ | 'istanbul'` | 'v8' |
reporter | string[] | ['text', 'html', 'clover', 'json'] | 出力フォーマット |
include | string[] | 全ファイル | カバレッジ対象 |
exclude | string[] | — | カバレッジ除外 |
thresholds | object | — | 最低カバレッジ率(lines, branches, functions, statements) |
reportsDirectory | string | './coverage' | 出力ディレクトリ |
all | boolean | false | 未カバーファイルもレポートに含める |
プール
| Pool | Description |
|---|---|
threads | Worker Threads(共有メモリ、高速) |
forks | 子プロセス(分離、ネイティブモジュール向き) |
vmThreads | Worker Threads + VM 分離 |
vmForks | 子プロセス + VM 分離 |
ブラウザモードオプション (test.browser)
| Option | Type | Default | Description |
|---|---|---|---|
browser.enabled | boolean | false | ブラウザモードの有効化 |
browser.headless | boolean | CI: true | ヘッドレスモード |
browser.provider | `'playwright' \ | 'webdriverio' \ | 'preview'` |
browser.instances | object[] | — | 実行するブラウザ一覧(例: [{ browser: 'chromium' }]) |
browser.ui | boolean | true | ブラウザ UI の表示 |
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }],
},
},
})globals: true の TypeScript 設定
// tsconfig.json
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}setupFiles vs globalSetup
setupFiles: テスト環境内で各ファイルの前に実行globalSetup: メイン Node プロセスで全スイートの前に一度だけ実行
関連
- CLI
- テスト環境
- カバレッジ
カバレッジ
プロバイダ
| Provider | Description |
|---|---|
| v8(デフォルト) | V8 ネイティブカバレッジ。高速。Node.js, Deno, Chromium ブラウザ対応 |
| istanbul | ソースコード計装ベース。全ランタイム対応。計装オーバーヘッドあり |
セットアップ
# v8(デフォルト)
npm i -D @vitest/coverage-v8
# istanbul
npm i -D @vitest/coverage-istanbul// vitest.config.ts
export default defineConfig({
test: {
coverage: {
provider: 'v8', // or 'istanbul'
},
},
})実行
vitest run --coverage.enabledまたは設定で有効化:
coverage: {
enabled: true,
}主要オプション
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
include: ['src/**/*.{ts,tsx}'],
exclude: ['src/**/*.d.ts', 'src/types/**'],
thresholds: {
lines: 80,
branches: 70,
functions: 80,
statements: 80,
},
reportsDirectory: './coverage',
all: true, // 未カバーファイルもレポートに含める
}カバレッジ無視コメント
/* v8 ignore next */
const result = condition ? 'a' : 'b'
/* v8 ignore start -- @preserve */
// このブロックはカバレッジから除外
/* v8 ignore stop -- @preserve */
/* istanbul ignore if -- @preserve */
if (unlikely) { /* ... */ }関連
- 設定
- CLI
テスト環境
組み込み環境
| Environment | Description |
|---|---|
node(デフォルト) | Node.js 環境 |
jsdom | jsdom パッケージによるブラウザ API エミュレーション |
happy-dom | jsdom より高速なブラウザ API エミュレーション(API は少なめ) |
edge-runtime | Vercel Edge Runtime エミュレーション |
グローバル設定
// vitest.config.ts
export default defineConfig({
test: {
environment: 'jsdom',
},
})ファイル単位の環境指定
テストファイル先頭のコメントで個別に環境を上書きできる。
// @vitest-environment jsdom
import { expect, test } from 'vitest'
test('DOM test', () => {
expect(typeof window).not.toBe('undefined')
})glob パターンでの環境指定
// vitest.config.ts
export default defineConfig({
test: {
environmentMatchGlobs: [
['**/*.dom.test.ts', 'jsdom'],
['**/*.node.test.ts', 'node'],
['**/*.edge.test.ts', 'edge-runtime'],
],
},
})カスタム環境
vitest-environment-${name} パッケージまたはファイルパスで独自環境を作成可能。
// vitest-environment-custom.ts
export default {
name: 'custom',
transformMode: 'ssr',
setup() {
// 環境セットアップ
return {
teardown() {
// クリーンアップ
},
}
},
}関連
- 設定
- ワークスペース
guide
| Name | Description | Path |
|---|---|---|
| CLI コマンド | デフォルト: dev では watch モード、CI では run モード | cli.md |
| 設定 (vitest.config.ts) | vitest.config.ts(または vite.config.ts の test キー)で設定 | config.md |
| カバレッジ | V8 ネイティブカバレッジ。高速。Node.js, Deno, Chromium… | coverage.md |
| テスト環境 | Node.js, jsdom, happy-dom, edge-runtime などの環境を切り替え | environment.md |
| スナップショットテスト | スナップショットファイルで実行結果を保存・比較 | snapshot.md |
| 型テスト | expectTypeOf と assertType で型レベルのテストを実行 | testing-types.md |
| ワークスペース | モノレポや異なるテスト設定を単一プロセスで実行 | workspace.md |
スナップショットテスト
基本
toMatchSnapshot()
値をスナップショットファイル(__snapshots__/ ディレクトリ)に保存し、次回以降の実行で比較する。
expect(result).toMatchSnapshot()
expect(result).toMatchSnapshot('optional hint')toMatchInlineSnapshot()
スナップショットをテストファイル内にインラインで保存する。初回実行時に Vitest が自動的にテストファイルを更新する。
expect({ a: 1, b: 2 }).toMatchInlineSnapshot(`
{
"a": 1,
"b": 2,
}
`)toMatchFileSnapshot()
指定ファイルパスとスナップショットを比較する(async)。HTML や JSON など構文ハイライトを活かしたい場合に有用。
await expect(htmlOutput).toMatchFileSnapshot('./snapshots/output.html')スナップショットの更新
# CLI フラグで更新
vitest run -u
# ウォッチモードでは 'u' キーを押すCI での挙動
process.env.CI が truthy の場合、スナップショットの書き込みは無効化される。 不一致・未作成・不要なスナップショットはテスト失敗になる。
カスタムシリアライザ
expect.addSnapshotSerializer({
serialize(val, config, indentation, depth, refs, printer) {
return `Pretty: ${printer(val.foo, config, indentation, depth, refs)}`
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo')
},
})または vitest.config.ts の snapshotSerializers で設定:
export default defineConfig({
test: {
snapshotSerializers: ['./my-serializer.ts'],
},
})エラースナップショット
expect(() => throwingFn()).toThrowErrorMatchingSnapshot()
expect(() => throwingFn()).toThrowErrorMatchingInlineSnapshot(`"error message"`)ARIA スナップショット(ブラウザモード・v4.1.4+)
ブラウザモードでのみ使用可能。DOM 要素のアクセシビリティツリーをキャプチャして比較する。
await expect.element(page.getByRole('navigation')).toMatchAriaInlineSnapshot(`
- navigation "Main":
- link "Home":
- /url: /
`)Playwright の ARIA スナップショット仕様に基づく。ブラウザモード専用のため、通常のテストでは使用不可。
関連
- Expect マッチャー
- CLI
型テスト
Vitest は expectTypeOf と assertType で型レベルのテストを行える。 *.test-d.ts ファイルが型テストとして自動認識される。
セットアップ
vitest typecheck
# または
vitest --typecheck// package.json
{
"scripts": {
"test:types": "vitest --typecheck"
}
}設定で typecheck.include を使ってマッチパターンをカスタマイズ可能。
expectTypeOf
流暢な API で型アサーションを行う。
import { expectTypeOf } from 'vitest'
expectTypeOf(42).toBeNumber()
expectTypeOf('hello').toBeString()
expectTypeOf(true).toBeBoolean()
expectTypeOf(undefined).toBeUndefined()
expectTypeOf(null).toBeNull()
expectTypeOf({}).toBeObject()
expectTypeOf([]).toBeArray()
expectTypeOf(() => {}).toBeFunction()型の等値・拡張チェック
expectTypeOf<string>().toEqualTypeOf<string>()
expectTypeOf<string>().toExtend<string | number>()
// 関数の引数・戻り値
expectTypeOf(fn).parameter(0).toBeString()
expectTypeOf(fn).returns.toBeNumber()NOT
expectTypeOf<string>().not.toBeNumber()assertType
TypeScript の型システムを直接利用するシンプルな方法。
import { assertType } from 'vitest'
const answer = 42
assertType<number>(answer)
// @ts-expect-error answer is not a string
assertType<string>(answer)仕組み
Vitest は内部で tsc または vue-tsc を呼び出し、結果をパースする。 ファイルは静的解析のみで実行されない。
ベストプラクティス
- 型引数を使うと推論より明確なエラーメッセージが得られる:
expectTypeOf(value).toEqualTypeOf<Expected>() @ts-expect-errorと組み合わせてランタイムテストにも含めるとタイポを検出できる--allowOnlyや-tフラグは型テストでも使用可能
関連
- 設定
- CLI
ワークスペース
Deprecated:vitest.workspace.tsによる独立ワークスペース設定ファイルは非推奨。vitest.config.tsのtest.projectsオプションへ移行する。
モノレポや異なるテスト設定を単一プロセスで実行するための機能。
基本設定
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
projects: ['packages/*'],
},
})プロジェクト検出
glob パターンにマッチするフォルダが個別プロジェクトとして扱われる。 config ファイルがなくてもプロジェクトとして認識される。
認識される config ファイル名:
vitest.config.ts/vite.config.jsvitest.unit.config.ts/vite.e2e.config.jsvitest.<name>.config.*
除外
projects: [
'packages/*',
'!packages/excluded',
]インライン設定
glob パターンとインライン設定を混在可能。
projects: [
'packages/*',
{
extends: true,
test: {
name: 'happy-dom',
environment: 'happy-dom',
include: ['tests/**/*.browser.test.{ts,js}'],
},
},
]設定の継承
extends オプション
extends: true でルートレベルの設定を継承:
{
extends: true,
test: {
name: 'unit',
include: ['**/*.unit.test.ts'],
},
}mergeConfig
import { defineProject, mergeConfig } from 'vitest/config'
import configShared from '../vitest.shared.js'
export default mergeConfig(
configShared,
defineProject({
test: { environment: 'jsdom' },
})
)プロジェクト指定実行
# 特定プロジェクト
npm run test -- --project e2e
# 複数プロジェクト
npm run test -- --project e2e --project unitプロジェクト設定で使えないオプション
coverage— プロセス全体設定のみreporters— ルートレベルのみresolveSnapshotPath— ルートのリゾルバが適用
関連
- 設定
- テスト環境
非同期テスト・フェイクタイマーパターン
async/await テスト
test('fetches data', async () => {
const data = await fetchData()
expect(data).toEqual({ id: 1, name: 'Alice' })
})Promise の resolves / rejects
test('resolves', async () => {
await expect(fetchData()).resolves.toEqual({ id: 1 })
})
test('rejects', async () => {
await expect(fetchBadData()).rejects.toThrow('not found')
})expect.assertions() でコールバック漏れを検出
非同期コールバック内のアサーションが確実に実行されることを保証する。
test('callback is called', async () => {
expect.assertions(1)
await new Promise<void>((resolve) => {
emitter.on('data', (value) => {
expect(value).toBe('expected')
resolve()
})
emitter.emit('data', 'expected')
})
})フェイクタイマー: 基本パターン
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.useRealTimers()
})
test('debounce fires after delay', () => {
const callback = vi.fn()
const debounced = debounce(callback, 300)
debounced()
expect(callback).not.toHaveBeenCalled()
vi.advanceTimersByTime(300)
expect(callback).toHaveBeenCalledOnce()
})フェイクタイマー: setInterval
test('interval fires repeatedly', () => {
const fn = vi.fn()
setInterval(fn, 1000)
vi.advanceTimersByTime(3000)
expect(fn).toHaveBeenCalledTimes(3)
})フェイクタイマー: 全タイマー実行
test('runs all pending timers', () => {
const fn1 = vi.fn()
const fn2 = vi.fn()
setTimeout(fn1, 100)
setTimeout(fn2, 200)
vi.runAllTimers()
expect(fn1).toHaveBeenCalled()
expect(fn2).toHaveBeenCalled()
})フェイクタイマー: 次のタイマーだけ進める
test('step through timers', () => {
const fn1 = vi.fn()
const fn2 = vi.fn()
setTimeout(fn1, 100)
setTimeout(fn2, 200)
vi.advanceTimersToNextTimer()
expect(fn1).toHaveBeenCalled()
expect(fn2).not.toHaveBeenCalled()
vi.advanceTimersToNextTimer()
expect(fn2).toHaveBeenCalled()
})フェイクタイマー: 日付のモック
test('mocks current date', () => {
vi.useFakeTimers()
vi.setSystemTime(new Date('2024-01-15T10:00:00Z'))
expect(new Date().toISOString()).toBe('2024-01-15T10:00:00.000Z')
expect(Date.now()).toBe(new Date('2024-01-15T10:00:00Z').getTime())
vi.useRealTimers()
})フェイクタイマー: 非同期タイマー
setTimeout 内で async 処理がある場合は async 版を使う。
test('async timer', async () => {
const fn = vi.fn()
setTimeout(async () => {
await someAsyncOp()
fn()
}, 1000)
await vi.advanceTimersByTimeAsync(1000)
expect(fn).toHaveBeenCalled()
})vi.waitFor(): リトライパターン
コールバックが成功するまで繰り返し実行する。
test('eventually becomes ready', async () => {
startProcess()
await vi.waitFor(() => {
expect(getStatus()).toBe('ready')
}, { timeout: 5000, interval: 100 })
})フェイクタイマーと組み合わせると、タイマーを自動的に進めながらリトライする。
vi.waitUntil(): 条件待ち
test('waits for condition', async () => {
const result = await vi.waitUntil(
() => checkCondition() ? { data: 'ready' } : undefined,
{ timeout: 3000 }
)
expect(result.data).toBe('ready')
})expect.poll(): ポーリングアサーション
test('polling assertion', async () => {
startAsyncProcess()
await expect.poll(() => getStatus(), {
interval: 100,
timeout: 5000,
}).toBe('complete')
})関連
- Vi ユーティリティ
- Expect マッチャー
- モックパターン
モックパターン
基本: vi.fn() でモック関数を作成
const handler = vi.fn()
handler('event')
expect(handler).toHaveBeenCalledWith('event')
expect(handler).toHaveBeenCalledTimes(1)戻り値の設定
const fetchUser = vi.fn()
.mockResolvedValueOnce({ id: 1, name: 'Alice' })
.mockResolvedValueOnce({ id: 2, name: 'Bob' })
.mockRejectedValue(new Error('not found'))
await fetchUser() // { id: 1, name: 'Alice' }
await fetchUser() // { id: 2, name: 'Bob' }
await fetchUser() // throws 'not found'モジュールモック: vi.mock()
オートモック
vi.mock('./user-service')
// 全エクスポートが vi.fn() になる
import { getUser } from './user-service'ファクトリモック
vi.mock('./api', () => ({
fetchData: vi.fn().mockResolvedValue([]),
API_URL: 'https://test.example.com',
}))部分モック(importOriginal)
一部のエクスポートだけをモックし、残りは本物を使う。
vi.mock('./utils', async (importOriginal) => {
const actual = await importOriginal<typeof import('./utils')>()
return {
...actual,
dangerousOp: vi.fn().mockReturnValue('safe'),
}
})vi.hoisted() と組み合わせ
vi.mock ファクトリはホイストされるため、外部変数を直接参照できない。 vi.hoisted() を使ってホイストレベルで変数を定義する。
const mockFetch = vi.hoisted(() => vi.fn())
vi.mock('./api', () => ({
fetchData: mockFetch,
}))
// テスト内で mockFetch を設定
mockFetch.mockResolvedValue({ data: [] })デフォルトエクスポートのモック
vi.mock('./config', () => ({
default: { apiUrl: 'https://test.example.com' },
}))オブジェクトモック: vi.mockObject()(v3.2.0+)
オブジェクトのメソッドを再帰的にモックする。インポートしたモジュールオブジェクトに対して vi.mock() なしで使える。
import { userService } from './user-service'
const mocked = vi.mockObject(userService)
mocked.getUser.mockResolvedValue({ id: 1, name: 'Alice' })元の実装を保持してスパイする場合:
const spied = vi.mockObject(userService, { spy: true })
// 元の実装を実行しつつ呼び出しを追跡
expect(spied.getUser).toHaveBeenCalled()
spied.getUser.mockRestore()オブジェクトスパイ: vi.spyOn()
元の実装を保持しつつ呼び出しを追跡する。
const spy = vi.spyOn(console, 'warn')
doSomething()
expect(spy).toHaveBeenCalledWith(expect.stringContaining('deprecated'))
spy.mockRestore()実装の差し替え
const spy = vi.spyOn(fs, 'readFileSync').mockReturnValue('mocked content')
// テスト後に復元
spy.mockRestore()getter/setter のスパイ
const obj = {
get value() { return 42 },
set value(v) { /* ... */ },
}
vi.spyOn(obj, 'value', 'get').mockReturnValue(100)
expect(obj.value).toBe(100)グローバル・環境変数のスタブ
// グローバル変数
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ data: [] }),
}))
// 環境変数
vi.stubEnv('NODE_ENV', 'test')
vi.stubEnv('API_KEY', 'test-key')
// afterEach で復元
afterEach(() => {
vi.unstubAllGlobals()
vi.unstubAllEnvs()
})モックのクリーンアップ
// beforeEach パターン
beforeEach(() => {
vi.clearAllMocks() // 履歴クリア(実装保持)
})
// または config で自動化
// vitest.config.ts: clearMocks: true / resetMocks: true / restoreMocks: true| Config Option | Effect |
|---|---|
clearMocks: true | 各テスト前に vi.clearAllMocks() |
resetMocks: true | 各テスト前に vi.resetAllMocks() |
restoreMocks: true | 各テスト前に vi.restoreAllMocks() |
関連
- Vi ユーティリティ
- Expect マッチャー
- 非同期テストパターン
patterns
| Name | Description | Path |
|---|---|---|
| 非同期テスト・フェイクタイマーパターン | async/await テスト、Promise の resolves / rejects、フェイクタイマーの基本… | async.md |
| モックパターン | vi.fn() でモック関数を作成。戻り値設定、モジュールモック、部分モック。 | mocking.md |
Basic Test
Write and run a minimal Vitest test from scratch.
// sum.ts
export function sum(a: number, b: number): number {
return a + b
}
// sum.test.ts
import { expect, test } from 'vitest'
import { sum } from './sum'
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3)
})// package.json
{
"scripts": {
"test": "vitest",
"test:run": "vitest run"
}
}Notes
viteststarts in watch mode by default;vitest runexecutes once and exits- Test files must include
.test.or.spec.in their filename - Minimum requirements: Vite ≥ 6.0.0 and Node ≥ 20.0.0
- No config file is needed for basic usage — Vitest reads
vite.config.*automatically
Coverage Setup
Measure code coverage with @vitest/coverage-v8 or @vitest/coverage-istanbul.
npm i -D @vitest/coverage-v8// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
include: ['src/**/*.{ts,tsx}'],
exclude: ['src/**/*.d.ts', 'src/**/*.test.ts'],
thresholds: {
lines: 80,
functions: 80,
branches: 70,
statements: 80,
},
},
},
})// package.json
{
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage"
}
}// Ignore specific lines from coverage
function riskyPath() {
/* v8 ignore next 3 -- @preserve */
if (process.env.NODE_ENV === 'debug') {
console.log('debug mode')
}
}Notes
v8is faster and requires no instrumentation;istanbulsupports any JS runtime and is more battle-testedreporter: ['text', 'html']prints a summary in the terminal and writes an HTML report tocoverage/thresholdsfail the run if coverage drops below the specified percentages — useful in CI- Use
/* v8 ignore ... */or/* istanbul ignore ... */comments to exclude unreachable branches
Describe and Hooks
Group related tests with describe and manage setup/teardown with lifecycle hooks.
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from 'vitest'
describe('UserService', () => {
let db: Database
beforeAll(async () => {
db = await Database.connect()
})
afterAll(async () => {
await db.disconnect()
})
beforeEach(() => {
db.seed()
})
afterEach(() => {
db.clean()
})
test('finds a user by id', async () => {
const user = await db.findUser(1)
expect(user).toMatchObject({ id: 1, name: 'Alice' })
})
test('throws when user not found', async () => {
await expect(db.findUser(999)).rejects.toThrow('not found')
})
})Notes
beforeAll/afterAllrun once perdescribeblock;beforeEach/afterEachrun around every testdescribeblocks can be nested; hooks apply to all tests in their scope- Use
test.skipto temporarily disable a test,test.onlyto run only that test during debugging describe.concurrentruns all tests within the block in parallel
Mocking Modules
Replace module exports with controlled fakes using vi.mock.
import { beforeEach, expect, test, vi } from 'vitest'
import { notifyUser } from './notifications'
// Auto-mock: all exports become vi.fn()
vi.mock('./notifications')
test('calls notifyUser with the correct message', () => {
notifyUser('welcome')
expect(notifyUser).toHaveBeenCalledWith('welcome')
})// Factory mock: supply a custom implementation
import { expect, test, vi } from 'vitest'
import { fetchProfile } from './profile'
vi.mock('./api', () => ({
fetchProfile: vi.fn().mockResolvedValue({ id: 1, name: 'Alice' }),
}))
test('renders the fetched profile', async () => {
const profile = await fetchProfile(1)
expect(profile.name).toBe('Alice')
})// Partial mock: keep real exports, override selected ones
vi.mock('./utils', async (importOriginal) => {
const actual = await importOriginal<typeof import('./utils')>()
return {
...actual,
dangerousOp: vi.fn().mockReturnValue('safe'),
}
})// vi.hoisted: share a mock reference across the hoisted vi.mock factory
const mockSend = vi.hoisted(() => vi.fn())
vi.mock('./mailer', () => ({ send: mockSend }))
test('sends an email', () => {
mockSend.mockResolvedValueOnce({ ok: true })
sendWelcomeEmail('user@example.com')
expect(mockSend).toHaveBeenCalledOnce()
})Notes
vi.mockis automatically hoisted to the top of the file — factory functions cannot close over variables defined after the call- Use
vi.hoisted()to define variables that need to be available inside the factory - Default exports must be keyed as
defaultin the factory object - Call
vi.clearAllMocks()inbeforeEach, or setclearMocks: truein config, to reset call history between tests
Parameterized Tests
Run the same test logic against multiple inputs using test.each.
import { expect, test } from 'vitest'
// Array-style: values are spread as arguments
test.each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
])('add(%i, %i) -> %i', (a, b, expected) => {
expect(a + b).toBe(expected)
})
// Object-style: named properties improve readability
test.each([
{ a: 1, b: 1, expected: 2 },
{ a: 1, b: 2, expected: 3 },
{ a: 2, b: 1, expected: 3 },
])('add($a, $b) -> $expected', ({ a, b, expected }) => {
expect(a + b).toBe(expected)
})// describe.each: parameterize an entire suite
import { describe, expect, test } from 'vitest'
describe.each([
{ currency: 'USD', symbol: '$' },
{ currency: 'EUR', symbol: '€' },
])('Currency $currency', ({ currency, symbol }) => {
test('has the correct symbol', () => {
expect(getSymbol(currency)).toBe(symbol)
})
})Notes
%iformats as integer,%sas string,%oas object in array-style test names$propertyNameinterpolates object properties into test names in object-styletest.eachanddescribe.eachboth accept a tagged template literal as an alternative syntax- Each row generates an independent test case shown separately in reports
samples
| Name | Description | Path |
|---|---|---|
| Basic Test | Write and run a minimal Vitest test from scratch. | basic-test.md |
| Coverage Setup | Measure code coverage with @vitest/coverage-v8 or @vitest/coverage-istanbul. | coverage-setup.md |
| Describe and Hooks | Group related tests with describe and manage setup/teardown with lifecycle hooks. | describe-and-hooks.md |
| Mocking Modules | Replace module exports with controlled fakes using vi.mock. | mocking-modules.md |
| Parameterized Tests | Run the same test logic against multiple inputs using test.each. | parameterized-tests.md |
| Snapshot Testing | Detect unintended output changes by comparing values against stored snapshots. | snapshot-testing.md |
| Spy on Methods | Track calls to existing methods with vi.spyOn without replacing the implementation. | spy-on-methods.md |
| Test Environment | Configure the runtime environment for tests (Node, jsdom, happy-dom, edge-runtime). | test-environment.md |
| Type Testing | Assert TypeScript types at compile time using expectTypeOf and assertType. | type-testing.md |
| Workspace Setup | Run tests across multiple packages in a monorepo with a single Vitest process. | workspace-setup.md |
Snapshot Testing
Detect unintended output changes by comparing values against stored snapshots.
import { expect, test } from 'vitest'
import { renderCard } from './card'
// External snapshot: stored in __snapshots__/<file>.snap
test('renders the card component', () => {
const html = renderCard({ title: 'Hello', body: 'World' })
expect(html).toMatchSnapshot()
})// Inline snapshot: stored directly in the test file
test('serializes the config object', () => {
const config = buildConfig({ env: 'test' })
expect(config).toMatchInlineSnapshot(`
{
"debug": false,
"env": "test",
"retries": 3,
}
`)
})// File snapshot: compare against an arbitrary file (async)
test('generates the report HTML', async () => {
const html = await generateReport()
await expect(html).toMatchFileSnapshot('./snapshots/report.html')
})// Error snapshot
test('throws a descriptive error', () => {
expect(() => parseConfig('')).toThrowErrorMatchingInlineSnapshot(
`"Config string must not be empty"`,
)
})Notes
- Run
vitest run -u(or pressuin watch mode) to update snapshots when output changes intentionally - In CI (
process.env.CIis truthy) snapshot writes are disabled — mismatches fail the test - Commit
.snapfiles alongside source code so reviewers can see output changes in PRs toMatchInlineSnapshotis preferred for small values;toMatchSnapshotfor large outputs such as HTML
Spy on Methods
Track calls to existing methods with vi.spyOn without replacing the implementation.
import { afterEach, expect, test, vi } from 'vitest'
test('logs a deprecation warning', () => {
const spy = vi.spyOn(console, 'warn')
callDeprecatedApi()
expect(spy).toHaveBeenCalledWith(expect.stringContaining('deprecated'))
spy.mockRestore()
})// Replace implementation while keeping the spy
import { fs } from 'node:fs'
test('reads the config file', () => {
const spy = vi.spyOn(fs, 'readFileSync').mockReturnValue('{"port":3000}')
const config = loadConfig()
expect(config.port).toBe(3000)
spy.mockRestore()
})// Spy on a getter
const store = {
get isLoggedIn() {
return checkSession()
},
}
test('renders the dashboard when logged in', () => {
vi.spyOn(store, 'isLoggedIn', 'get').mockReturnValue(true)
const result = renderApp()
expect(result).toContain('Dashboard')
})Notes
spy.mockRestore()reverts the method to its original implementation; call it inafterEachor inline- Setting
restoreMocks: trueinvitest.config.tsrestores all spies automatically before each test vi.spyOnwraps the existing function, so the original is still called unless.mockImplementation()or.mockReturnValue()is used- Spy assertions include
toHaveBeenCalled,toHaveBeenCalledTimes,toHaveBeenCalledWith, andtoHaveBeenLastCalledWith
Test Environment
Configure the runtime environment for tests (Node, jsdom, happy-dom, edge-runtime).
// vitest.config.ts — set the default environment for all tests
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environment: 'jsdom', // 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime'
},
})// Per-file override using a control comment
// @vitest-environment happy-dom
import { expect, test } from 'vitest'
test('window is defined', () => {
expect(typeof window).not.toBe('undefined')
})// vitest.config.ts — different environments per file pattern
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environmentMatchGlobs: [
['**/*.dom.test.ts', 'jsdom'],
['**/*.node.test.ts', 'node'],
],
},
})Notes
nodeis the default; usejsdomorhappy-domwhen testing code that requires browser globals (window,document)happy-domis generally faster thanjsdombut has lower API coverageedge-runtimeemulates the Vercel Edge environment (no Node built-ins)- Install the required package for each environment:
npm i -D jsdom/npm i -D happy-dom
Type Testing
Assert TypeScript types at compile time using expectTypeOf and assertType.
// math.test-d.ts
import { assertType, expectTypeOf, test } from 'vitest'
import { add, multiply } from './math'
test('add returns a number', () => {
expectTypeOf(add).toBeFunction()
expectTypeOf(add).parameter(0).toBeNumber()
expectTypeOf(add).returns.toBeNumber()
})
test('multiply result is assignable to number', () => {
assertType<number>(multiply(2, 3))
// @ts-expect-error — multiply does not accept strings
assertType<number>(multiply('a', 'b'))
})// expectTypeOf: structural type equality
import { expectTypeOf, test } from 'vitest'
import type { User } from './types'
test('User has the expected shape', () => {
expectTypeOf<User>().toEqualTypeOf<{ id: number; name: string }>()
})
test('User id is a number, not string', () => {
expectTypeOf<User['id']>().not.toBeString()
expectTypeOf<User['id']>().toBeNumber()
})// package.json — run type checks alongside unit tests
{
"scripts": {
"test": "vitest",
"typecheck": "vitest --typecheck"
}
}Notes
- Type test files use the
.test-d.tsextension by default; change viatypecheck.includein config - Files are statically analyzed by
tsc(orvue-tsc), not executed at runtime expectTypeOfprovides detailed error messages showing actual vs. expected types@ts-expect-errorinsideassertTypeverifies that an assignment is intentionally invalid
Workspace Setup
Run tests across multiple packages in a monorepo with a single Vitest process.
// vitest.config.ts (root)
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
projects: [
'packages/*', // auto-discovers vitest.config.* in each package
'!packages/excluded', // exclude specific packages
],
},
})// vitest.config.ts (root) — inline project configs
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
projects: [
{
extends: true, // inherit root config
test: {
name: 'unit',
include: ['packages/*/src/**/*.test.ts'],
environment: 'node',
},
},
{
extends: true,
test: {
name: 'dom',
include: ['packages/*/src/**/*.dom.test.ts'],
environment: 'happy-dom',
},
},
],
},
})# Run all projects
npx vitest
# Run a specific project by name
npx vitest --project unit
npx vitest --project domNotes
- Each project must have a unique
name; Vitest throws an error on duplicates extends: truemerges the root config into the inline project config- Project configs must be named
vitest.config.*,vite.config.*, orvitest.<name>.config.* - The workspace feature replaces the older
vitest.workspace.tsfile — useprojectsinsidevitest.config.tsinstead
cli
vitest CLI コマンドとフラグ一覧。
テストの実行(watch モード)
vitest開発環境では watch モード、CI 環境(process.env.CI が truthy)では自動的に run モードで起動する。
テストの単一実行(CI 向け)
vitest runwatch モードを無効にして一度だけ実行する。
watch モードの明示的な起動
vitest watchvitest dev はエイリアス。
ベンチマークテストの実行
vitest bench型チェックテストの実行
vitest typecheckマッチするテスト一覧の表示
vitest listJSON 形式で出力する場合:
vitest list --jsonファイル一覧のみ出力する場合:
vitest list --filesOnly特定ファイルに関連するテストの実行
vitest related src/utils.ts--run を組み合わせて lint-staged と連携する場合:
vitest related src/utils.ts --runファイル名で絞り込み
vitest foobarファイル名と行番号で絞り込み(v3 以降)
vitest basic/foo.test.ts:10テスト名パターンで絞り込み
vitest run -t "should handle errors"vitest run --testNamePattern "should handle errors"変更ファイルに関連するテストのみ実行
vitest run --changed特定コミットやブランチからの変更を対象にする場合:
vitest run --changed HEAD~1スナップショットの更新
vitest run -uvitest run --update警告: スナップショットが上書きされる。CI 環境ではデフォルトでスナップショット書き込みが防止される。
カバレッジ付き実行
vitest run --coveragevitest run --coverage.enabledUI を有効にして起動
vitest --uiブラウザで http://localhost:51204/__vitest__/ にアクセスする。
ブラウザモードでの実行
npx vitest --browser=chromiumヘッドレスモードで実行する場合:
npx vitest --browser.headless特定プロジェクトのみ実行(workspace 構成)
npm run test -- --project e2e複数プロジェクトを指定する場合:
npm run test -- --project e2e --project unitn 個の失敗で実行を停止
vitest run --bail 3テストなしでも成功終了
vitest run --passWithNoTestsシャーディング(並列 CI 分散実行)
vitest run --shard 1/3シェル補完スクリプトの生成
vitest complete zshvitest complete bashヘルプの表示
npx vitest --helpcoverage
カバレッジ収集・レポート生成コマンド。
カバレッジ付きテストの実行
vitest run --coveragevitest run --coverage.enabledカバレッジプロバイダーの指定
v8 プロバイダー(デフォルト)を使用する場合:
vitest run --coverage --coverage.provider v8istanbul プロバイダーを使用する場合:
vitest run --coverage --coverage.provider istanbulカバレッジレポートの出力先指定
vitest run --coverage --coverage.reportsDirectory ./coverageカバレッジ対象ファイルの指定
vitest run --coverage --coverage.include "src/**"カバレッジ対象外ファイルの指定
vitest run --coverage --coverage.exclude "src/**/*.test.ts"全カバレッジ閾値を 100% に設定して実行
警告: 閾値を満たさない場合、テストが失敗する。
vitest run --coverage --coverage.thresholds.100カバレッジレポーターの指定
vitest run --coverage --coverage.reporter text --coverage.reporter htmlinstall
vitest および関連パッケージのインストール。
vitest 本体のインストール
npm install -D vitestyarn add -D vitestpnpm add -D vitestbun add -D vitestインストールなしで直接実行する場合は npx vitest を使用する。
カバレッジプロバイダーのインストール(v8)
npm install -D @vitest/coverage-v8pnpm add -D @vitest/coverage-v8カバレッジプロバイダーのインストール(istanbul)
npm install -D @vitest/coverage-istanbulpnpm add -D @vitest/coverage-istanbulVitest UI のインストール
npm install -D @vitest/uipnpm add -D @vitest/uiブラウザモード(Playwright プロバイダー)のインストール
npm install -D vitest @vitest/browser-playwrightyarn add -D vitest @vitest/browser-playwrightpnpm add -D vitest @vitest/browser-playwrightbun add -D vitest @vitest/browser-playwrightブラウザモード(Preview プロバイダー)のインストール
CI 環境での使用は非推奨。
npm install -D vitest @vitest/browser-previewpnpm add -D vitest @vitest/browser-previewscripts
| Name | Description | Path |
|---|---|---|
| cli | vitest CLI コマンドとフラグ一覧。 | cli.md |
| coverage | カバレッジ収集・レポート生成コマンド。 | coverage.md |
| install | vitest および関連パッケージのインストール。 | install.md |
| setup | vitest のプロジェクト初期設定コマンド。 | setup.md |
setup
vitest のプロジェクト初期設定コマンド。
ブラウザモードの初期設定
npx vitest init browseryarn exec vitest init browserpnpx vitest init browserbunx vitest init browservitest.config.ts にブラウザモード設定を生成する。
package.json へのテストスクリプト追加
package.json の scripts セクションに以下を追加する:
# package.json に手動で追記する設定例(コマンドではなく設定値)
# "test": "vitest"
# "coverage": "vitest run --coverage"実際の package.json 設定(コマンドで直接追加する場合は npm pkg set 等を利用):
npm pkg set scripts.test="vitest"
npm pkg set scripts.coverage="vitest run --coverage"