vitest-testing について
vitest-testingは、Viteプロジェクトのテストを支援するスキルです。TypeScriptやJavaScriptのユニットテスト、統合テストを作成・設定する開発者向けに、Vitestの設定、カバレッジ、モック、jsdomの注意点を解説します。
主な機能
- 01ViteとVitestの設定
- 02TypeScriptとJavaScriptのテスト作成
- 03モック、スパイ、スナップショットの活用
- 04カバレッジとベンチマークの実行
- 05jsdomのアサーション上の落とし穴の特定
活用シーン
- 01Viteプロジェクトにテストランナーを設定する
- 02Jestからテストを移行する
- 03ユニットテストや統合テストを書く
- 04カバレッジを確認し無効なDOMアサーションを避ける
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ laurigates/ claude-plugins --skill vitest-testingインストール後、Claude Code で/vitest-testingを入力して読み込まれたか確認します。
同梱ファイルREFERENCE.md
詳細
Vitest のテスト
Vitest は、Vite プロジェクト向けに設計された最新のテストランナーです。高速で、ESM をネイティブにサポートし、Jest 互換の API に加えて、優れた TypeScript サポートと HMR を活用した即時ウォッチモードを提供します。
このスキルを使う場面
| 次の場合にこのスキルを使います… | 次の場合は別のスキルを使います… |
|---|---|
| Vitest をセットアップまたは設定する | E2E ブラウザーテストを作成する(playwright-testing を使用) |
| TS/JS でユニットテストや統合テストを作成する | Python コードをテストする(python-testing を使用) |
| Jest から Vitest に移行する | テスト品質を分析する(test-quality-analysis を使用) |
| カバレッジのしきい値を設定する | プロパティベーステストを生成する(property-based-testing を使用) |
| モック、スパイ、またはフェイクタイマーを使う | テストの有効性を検証する(mutation-testing を使用) |
主な専門分野
- Vite ネイティブ:Vite の設定、変換、プラグインを再利用します
- 高速:HMR を活用したウォッチモードで即座にフィードバックを得られます
- Jest 互換:同様の API を備えたドロップイン置換です
- TypeScript:TypeScript を第一級にサポートします
- ESM:ESM をネイティブにサポートし、トランスパイルは不要です
- jsdom の DOM テスト:および、その中でひそかに嘘をつくアサーション(以下を参照)
インストール
bun add --dev vitest
bun add --dev @vitest/coverage-v8 # Coverage (recommended)
bun add --dev happy-dom # DOM testing (optional)
bunx vitest --version # Verify
設定(vitest.config.ts)
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
},
});
基本的なコマンド
bunx vitest # Watch mode (default)
bunx vitest run # Run once (CI mode)
bunx vitest --coverage # With coverage
bunx vitest src/utils.test.ts # Specific file
bunx vitest -t "should add numbers" # Filter by name
bunx vitest related src/utils.ts # Related tests
bunx vitest -u # Update snapshots
bunx vitest bench # Benchmarks
bunx vitest --ui # UI mode
テストの作成
基本的なテスト構造
import { describe, it, expect } from 'vitest';
import { add, multiply } from './math';
describe('math utils', () => {
it('should add two numbers', () => {
expect(add(2, 3)).toBe(5);
});
it('should multiply two numbers', () => {
expect(multiply(2, 3)).toBe(6);
});
});
主なアサーション
| アサーション | 説明 |
|---|---|
toBe(value) | 厳密な等価性 |
toEqual(value) | 深い等価性 |
toStrictEqual(value) | 深い厳密な等価性 |
toBeTruthy() / toBeFalsy() | 真偽値としての評価 |
toBeNull() / toBeUndefined() | null のチェック |
toBeGreaterThan(n) / toBeLessThan(n) | 数値の比較 |
toBeCloseTo(n) | 浮動小数点数の比較 |
toMatch(regex) / toContain(str) | 文字列の照合 |
toHaveLength(n) | 配列/文字列の長さ |
toHaveProperty(key) | オブジェクトのプロパティ |
toMatchObject(obj) | オブジェクトの部分一致 |
toThrow(msg) | エラーのスロー |
非同期テスト
test('async test', async () => {
const data = await fetchData();
expect(data).toBe('expected');
});
test('promise resolves', async () => {
await expect(fetchData()).resolves.toBe('expected');
});
test('promise rejects', async () => {
await expect(fetchBadData()).rejects.toThrow('error');
});
jsdom での DOM テスト — 嘘をつくアサーション
environment: 'jsdom' では、レイアウトエンジンのない DOM が提供され、CSS パーサーもブラウザーより対応範囲が狭くなります。そのため、見た目には自然なアサーションの中には空虚なものがあり、捕捉するために書いたバグがあってもテストに通り、カバレッジとして記録されるため、誰も再確認しなくなります。
| 落とし穴 | バグがあってもテストに通る理由 | 代わりに検証するもの |
|---|---|---|
スタイルシートで設定されたスタイルに対する el.style.overflowY | インラインスタイルは空で、宣言はクラスルール内にあります | getComputedStyle(el).overflowY — jsdom は挿入された <style> ルールを解決します |
min() / calc() の値に対する getComputedStyle(el).width | jsdom のパーサーは宣言全体をひそかに破棄し、どちらの場合も ""/0 を返します | スタイルシートのソーステキストを確認するか、実ブラウザーに任せます |
| サイズや位置に関するあらゆる検証 | getBoundingClientRect() はすべてゼロを返します。レイアウトはありません | 実ブラウザーのテスト層 |
async にレンダリングするものをクリックした直後のアサーション | パネルはまだ空なので、「不正な要素が見つからない」は自明に真となります | フラッシュ(await new Promise(r => setTimeout(r, 0)))してから、実際のチェックの前にコンテナーが空でないことを検証します |
また、jsdom には Element.prototype.scrollIntoView が存在しないため、要素を中央に配置するコードパスはマウント時に例外をスローします。これをスタブ化してください(Element.prototype.scrollIntoView = () => {})。これはハーネスの不足であり、動作の変更ではありません。
また、条件付きアサーションは決して書かないでください。
// Passes silently in exactly the case it was meant to catch — a renamed class.
if (found.length === 1) expect(found[0].textContent).toMatch(/x/);
// Assert unconditionally.
expect(found).toHaveLength(1);
expect(found[0].textContent).toMatch(/x/);
わざと失敗させる
一度も失敗したことのない回帰テストは、何かをテストしていると証明されていません。信頼する前に、意図的に失敗させてメッセージを読んでください。
- このパッケージ内の修正 → 修正をその場で元に戻し、正しい理由で失敗することを確認してから、修正を戻します。
- パッケージ横断のテストスイート → 依存関係を修正前のリリースに再固定し、失敗を確認してから、固定を戻します。
確認された失敗出力を PR 本文に記録してください。「失敗する」は主張であり、メッセージが証拠です。
実際の統合テストのために兄弟パッケージのソースを読み込む
不具合が2つのパッケージを組み合わせた場合に発生するなら、それぞれを代替物に対してテストしても、両方のテストは通る一方で、組み合わせると動作しない状態が見逃されます。兄弟パッケージの実際のソースを読み込むには、次の手順を実行します。
- リリースタグに固定して依存させます(例:git dependency)。これにより、テストスイートは変化するブランチではなく、公開済みの成果物をテストします。
server.deps.inlineに追加します。vitest はデフォルトでnode_modulesを外部化するため、Node に生の TypeScript が渡されてしまいます。
export default defineConfig({
test: { server: { deps: { inline: [/sibling-package/] } } },
});
- ホストの実際のエントリーポイントをエクスポートします。そうすることで、不具合が発生しない単体テスト用の接続点ではなく、実際のコードパスをテストスイートで実行できます。
モック(必須パターン)
import { vi, test, expect } from 'vitest';
// Mock function
const mockFn = vi.fn();
mockFn.mockReturnValue(42);
// Mock module
vi.mock('./api', () => ({
fetchUser: vi.fn(() => Promise.resolve({ id: 1, name: 'John' })),
}));
// Mock timers
vi.useFakeTimers();
vi.advanceTimersByTime(1000);
vi.restoreAllMocks();
// Spy on method
const spy = vi.spyOn(object, 'method');
スナップショットテスト
test('snapshot test', () => {
expect(data).toMatchSnapshot();
});
test('inline snapshot', () => {
expect(result).toMatchInlineSnapshot('5');
});
// Update snapshots: bunx vitest -u
カバレッジ
bun add --dev @vitest/coverage-v8
bunx vitest --coverage
主な設定オプション:provider、reporter、include、exclude、thresholds。
エージェント向けの最適化
| コンテキスト | コマンド |
|---|---|
| クイックテスト | bunx vitest --reporter=dot --bail=1 |
| CI テスト | bunx vitest run --reporter=junit |
| カバレッジチェック | bunx vitest --coverage --reporter=dot |
| 単一ファイル | bunx vitest run src/utils.test.ts --reporter=dot |
| 失敗したテストのみ | bunx vitest --changed --bail=1 |
詳細な例、高度なパターン、ベストプラクティスについては、REFERENCE.md を参照してください。
参考資料
- 公式ドキュメント:https://vitest.dev
- 設定:https://vitest.dev/config/
- API リファレンス:https://vitest.dev/api/
- Jest からの移行:https://vitest.dev/guide/migration.html
- カバレッジ:https://vitest.dev/guide/coverage.html