Vitest高度なテスト技法ガイド - モック、スナップショット、カバレッジ活用術
Vitest高度なテスト技法ガイド
VitestはViteベースの超高速テストフレームワークです。Jestとの互換性を保ちながら、ESMネイティブサポート、TypeScript標準対応、そして驚異的な実行速度を実現しています。
Vitestの基本セットアップ
npm install -D vitest @vitest/ui
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true, // describe, it, expect等をグローバルに
environment: 'jsdom', // DOM環境のシミュレーション
setupFiles: './test/setup.ts', // セットアップファイル
coverage: {
provider: 'v8', // カバレッジプロバイダー
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'test/',
'**/*.spec.ts',
'**/*.test.ts',
],
},
testTimeout: 10000, // タイムアウト設定
},
})
モックの高度なテクニック
関数のモック
import { vi, describe, it, expect, beforeEach } from 'vitest'
describe('Advanced Mocking', () => {
// 基本的なモック
it('should mock a function', () => {
const mockFn = vi.fn()
mockFn('hello')
expect(mockFn).toHaveBeenCalledWith('hello')
expect(mockFn).toHaveBeenCalledTimes(1)
})
// 戻り値の設定
it('should return mocked value', () => {
const mockFn = vi.fn()
.mockReturnValueOnce('first')
.mockReturnValueOnce('second')
.mockReturnValue('default')
expect(mockFn()).toBe('first')
expect(mockFn()).toBe('second')
expect(mockFn()).toBe('default')
expect(mockFn()).toBe('default')
})
// 非同期モック
it('should mock async function', async () => {
const mockAsync = vi.fn()
.mockResolvedValueOnce({ id: 1, name: 'User 1' })
.mockResolvedValueOnce({ id: 2, name: 'User 2' })
.mockRejectedValueOnce(new Error('Failed'))
await expect(mockAsync()).resolves.toEqual({ id: 1, name: 'User 1' })
await expect(mockAsync()).resolves.toEqual({ id: 2, name: 'User 2' })
await expect(mockAsync()).rejects.toThrow('Failed')
})
})
タイマーのモック
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'
describe('Timer Mocking', () => {
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.restoreAllMocks()
})
it('should fast-forward timers', () => {
const callback = vi.fn()
setTimeout(callback, 1000)
expect(callback).not.toHaveBeenCalled()
vi.advanceTimersByTime(1000)
expect(callback).toHaveBeenCalledTimes(1)
})
it('should run all timers', () => {
const callback1 = vi.fn()
const callback2 = vi.fn()
setTimeout(callback1, 1000)
setTimeout(callback2, 5000)
vi.runAllTimers()
expect(callback1).toHaveBeenCalled()
expect(callback2).toHaveBeenCalled()
})
})
スナップショットテスト
基本的なスナップショット
import { describe, it, expect } from 'vitest'
describe('Snapshot Testing', () => {
it('should match snapshot', () => {
const data = {
id: 1,
name: 'John Doe',
email: 'john@example.com',
createdAt: new Date('2025-01-01'),
}
expect(data).toMatchSnapshot()
})
// インラインスナップショット
it('should match inline snapshot', () => {
const user = { id: 1, name: 'John' }
expect(user).toMatchInlineSnapshot(`
{
"id": 1,
"name": "John",
}
`)
})
})
カバレッジの最適化
// vitest.config.ts
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html', 'lcov'],
// カバレッジ基準
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
// 除外パターン
exclude: [
'node_modules/',
'dist/',
'test/',
'**/*.spec.ts',
'**/*.test.ts',
'**/types.ts',
'**/*.config.ts',
],
// 特定のファイルのみ
include: ['src/**/*.ts', 'src/**/*.tsx'],
// カバレッジレポートの出力先
reportsDirectory: './coverage',
},
},
})
実行:
# カバレッジ付きでテスト
vitest --coverage
# HTMLレポート確認
open coverage/index.html
UIモード(Vitest UI)
# UIモードで起動
vitest --ui
# ブラウザで http://localhost:51204/__vitest__/ を開く
Vitest UIでできること:
- テスト実行状況のビジュアル表示
- テストのフィルタリング・検索
- カバレッジレポートの確認
- テストファイルのソースコード表示
- ホットリロード
まとめ
Vitestは、高速で強力なテストフレームワークです。主な利点は以下の通りです。
- 超高速: Viteの恩恵を受けた高速実行
- 型安全: TypeScriptネイティブサポート
- Jest互換: 既存のJestテストも動く
- 優れたDX: UIモード、ウォッチモード、ホットリロード
- 包括的な機能: モック、スナップショット、カバレッジ
これらのテクニックを活用して、堅牢で保守性の高いテストスイートを構築しましょう。