テストコードをゼロから書かせるプロセス
このワークフローが有効な場面
Section titled “このワークフローが有効な場面”- テストがない(またはほとんどない)プロジェクトでテストを始めたいとき
- 既存の機能にテストを後付けしたいとき
- バグ修正後にリグレッションテストを書きたいとき
- テストの書き方がわからないが、カバレッジを上げたいとき
テスト追加の優先順位
Section titled “テスト追加の優先順位”全部のコードに一気にテストを書こうとすると挫折します。以下の優先順位で進めましょう。
- ビジネスロジック(Service層・ドメイン層): 一番価値が高い
- APIエンドポイント: 次に重要
- バグが発生した箇所: 再発防止として即座に書く
- UIコンポーネント: インタラクションがあるものを優先
※エンドポイント: APIにアクセスするためのURL。例: `/api/users` や `/api/posts/123` など。※API: Application Programming Interface の略。外部サービスとプログラム間でデータをやりとりするための接続口。
ステップバイステップの手順
Section titled “ステップバイステップの手順”ステップ1: テスト対象を選んでClaudeに分析させる
Section titled “ステップ1: テスト対象を選んでClaudeに分析させる”「src/services/discount.ts のテストを書きたいです。まず、このファイルを読んで以下を教えてください:1. テストすべき主要な関数・クロージャの一覧2. 各関数のテストケース案(正常系・異常系・エッジケース)3. テストが難しい部分(外部依存がある箇所など)
コードはまだ書かないで、テスト計画だけ出力してください。」ステップ2: テスト計画を確認する
Section titled “ステップ2: テスト計画を確認する”Claudeの出力例:
テスト計画:
src/services/discount.ts の主要関数:
1. calculateDiscount(price, couponCode) 正常系: - 有効なパーセンテージクーポン(10%、20%、50%) - 有効な固定額クーポン(500円引き、1000円引き) 異常系: - 無効なクーポンコード - 有効期限切れクーポン - 最低購入金額未満 エッジケース: - 割引後が0以下になる場合 - price が 0 の場合
2. validateCoupon(couponCode) ...
外部依存がある箇所:- データベースへのクーポン取得(モック化が必要)- 現在日時(Date.now()のモック化が必要)ステップ3: テストを実際に書かせる
Section titled “ステップ3: テストを実際に書かせる”「テスト計画を確認しました。calculateDiscount 関数のテストを実装してください。
条件:- テストフレームワーク: Vitest- ファイル: src/services/__tests__/discount.test.ts- データベースは vi.mock でモックする- 現在日時も vi.useFakeTimers でコントロールできるようにする」Claudeが生成するテスト例:
import { describe, it, expect, vi, beforeEach } from 'vitest';import { calculateDiscount } from '../discount';import { getCoupon } from '../../lib/db';
vi.mock('../../lib/db');const mockGetCoupon = vi.mocked(getCoupon);
describe('calculateDiscount', () => { beforeEach(() => { vi.clearAllMocks(); vi.useFakeTimers(); vi.setSystemTime(new Date('2026-01-15')); });
afterEach(() => { vi.useRealTimers(); });
describe('パーセンテージクーポン', () => { it('10%割引クーポンが正しく適用される', async () => { mockGetCoupon.mockResolvedValue({ code: 'SAVE10', type: 'percentage', value: 10, minPurchase: 0, expiresAt: new Date('2026-12-31'), });
const result = await calculateDiscount(10000, 'SAVE10');
expect(result.discountAmount).toBe(1000); expect(result.finalPrice).toBe(9000); });
it('割引後の価格が0未満にならない', async () => { mockGetCoupon.mockResolvedValue({ code: 'SAVE200', type: 'fixed', value: 200, minPurchase: 0, expiresAt: new Date('2026-12-31'), });
const result = await calculateDiscount(100, 'SAVE200');
expect(result.finalPrice).toBe(0); // 0未満にならない }); });
describe('エラーケース', () => { it('無効なクーポンコードはエラーを投げる', async () => { mockGetCoupon.mockResolvedValue(null);
await expect(calculateDiscount(10000, 'INVALID')).rejects.toThrow('クーポンが見つかりません'); });
it('有効期限切れクーポンはエラーを投げる', async () => { mockGetCoupon.mockResolvedValue({ code: 'EXPIRED', type: 'percentage', value: 10, minPurchase: 0, expiresAt: new Date('2026-01-01'), // 過去の日付 });
await expect(calculateDiscount(10000, 'EXPIRED')).rejects.toThrow('クーポンの有効期限が切れています'); }); });});ステップ4: テストを実行して確認する
Section titled “ステップ4: テストを実行して確認する”npx vitest run src/services/__tests__/discount.test.tsステップ5: カバレッジを確認して補完する
Section titled “ステップ5: カバレッジを確認して補完する”npx vitest run --coverage
# カバレッジレポートを確認open coverage/index.html「カバレッジレポートを確認しました。src/services/discount.ts の以下の行がカバーされていません:- 134行目: 最低購入金額のチェック- 156〜162行目: 複数クーポン適用の場合
この部分のテストを追加してください。」※モック: テスト時に本物の代わりに使う偽のオブジェクトや関数。外部APIや DBへの実際の通信を避けるために使う。
よく使うテストパターン
Section titled “よく使うテストパターン”非同期処理のテスト
Section titled “非同期処理のテスト”it('非同期処理のテスト', async () => { const result = await someAsyncFunction(); expect(result).toBe(expected);});モジュールのモック
Section titled “モジュールのモック”vi.mock('../api/client', () => ({ fetchData: vi.fn(),}));
const mockFetchData = vi.mocked(fetchData);mockFetchData.mockResolvedValue({ data: 'test' });Reactコンポーネントのテスト
Section titled “Reactコンポーネントのテスト”import { render, screen, userEvent } from '@testing-library/react';
it('ボタンをクリックすると値が増える', async () => { render(<Counter initialCount={0} />);
const button = screen.getByRole('button', { name: '増やす' }); await userEvent.click(button);
expect(screen.getByText('1')).toBeInTheDocument();});※非同期処理: 時間のかかる処理(API呼び出し・ファイル読み込みなど)を待機中でも他の処理を続ける方式。JavaScriptでは `async/await` や `Promise` を使う。
テスト追加を頼むときのプロンプトのコツ
Section titled “テスト追加を頼むときのプロンプトのコツ”「以下のコードのテストを書いてください。【テストの条件】- テストフレームワーク: [Vitest / Jest / RSpec 等]- モックライブラリ: [vi / jest / rspec-mocks 等]- テストファイルの場所: [パス]- 外部依存: [DB / API / ファイルシステム 等をモックする]
【テストケースとして含めてほしいもの】- 正常系: [入力と期待する出力]- 異常系: [エラーケース]- エッジケース: [境界値 等]」※プロンプト: AIへの指示文。Claude Codeへの指示の質がそのまま出力の質に影響する。
注意点・よくある失敗
Section titled “注意点・よくある失敗”失敗1: テストのためのテスト
Section titled “失敗1: テストのためのテスト”expect(true).toBe(true) のような意味のないテストは書かせないよう、「このテストが通れば実際に○○が動いていると言えるか確認してください」と念押しします。
失敗2: 実装の詳細を検証するテスト
Section titled “失敗2: 実装の詳細を検証するテスト”「この関数が3回呼ばれること」を検証するのではなく、「ユーザーに正しい結果が返ること」を検証します。