Skip to content

テストコードをゼロから書かせるプロセス

このワークフローが有効な場面

Section titled “このワークフローが有効な場面”
  • テストがない(またはほとんどない)プロジェクトでテストを始めたいとき
  • 既存の機能にテストを後付けしたいとき
  • バグ修正後にリグレッションテストを書きたいとき
  • テストの書き方がわからないが、カバレッジを上げたいとき

全部のコードに一気にテストを書こうとすると挫折します。以下の優先順位で進めましょう。

  1. ビジネスロジック(Service層・ドメイン層): 一番価値が高い
  2. APIエンドポイント: 次に重要
  3. バグが発生した箇所: 再発防止として即座に書く
  4. UIコンポーネント: インタラクションがあるものを優先
エンドポイント: APIにアクセスするためのURL。例: `/api/users` や `/api/posts/123` など。API: Application Programming Interface の略。外部サービスとプログラム間でデータをやりとりするための接続口。

ステップ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: テストを実行して確認する”
Terminal window
npx vitest run src/services/__tests__/discount.test.ts

ステップ5: カバレッジを確認して補完する

Section titled “ステップ5: カバレッジを確認して補完する”
Terminal window
npx vitest run --coverage
# カバレッジレポートを確認
open coverage/index.html
「カバレッジレポートを確認しました。
src/services/discount.ts の以下の行がカバーされていません:
- 134行目: 最低購入金額のチェック
- 156〜162行目: 複数クーポン適用の場合
この部分のテストを追加してください。」
モック: テスト時に本物の代わりに使う偽のオブジェクトや関数。外部APIや DBへの実際の通信を避けるために使う。
it('非同期処理のテスト', async () => {
const result = await someAsyncFunction();
expect(result).toBe(expected);
});
vi.mock('../api/client', () => ({
fetchData: vi.fn(),
}));
const mockFetchData = vi.mocked(fetchData);
mockFetchData.mockResolvedValue({ data: 'test' });
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への指示の質がそのまま出力の質に影響する。

expect(true).toBe(true) のような意味のないテストは書かせないよう、「このテストが通れば実際に○○が動いていると言えるか確認してください」と念押しします。

失敗2: 実装の詳細を検証するテスト

Section titled “失敗2: 実装の詳細を検証するテスト”

「この関数が3回呼ばれること」を検証するのではなく、「ユーザーに正しい結果が返ること」を検証します。