AgentSkillsCN

tdd

执行测试驱动开发(TDD)工作流的技能。适用时机:(1) 开始实现新功能时 (2) 修复Bug时 (3) 重构前 (4) 当被告知“先写测试”时 (5) 需要以质量为导向进行实现时。触发示例:“以TDD方式实现”“以测试驱动的方式”“先写测试”“按照RED-GREEN-REFACTOR流程”“为UserService编写测试”。

SKILL.md
--- frontmatter
name: tdd
description: >
  テスト駆動開発(TDD)ワークフローを実行するスキル。
  使用タイミング: (1) 新機能の実装開始時 (2) バグ修正時 (3) リファクタリング前
  (4) 「テストから書いて」と言われた時 (5) 品質重視の実装が必要な時。
  トリガー例: 「TDDで実装して」「テスト駆動で」「テストから書いて」
  「RED-GREEN-REFACTORで」「UserServiceのテストを書いて」

/tdd - Test-Driven Development

テスト駆動開発ワークフローを実行する。

使い方

code
/tdd                    # 対話的にTDDを開始
/tdd UserService        # UserServiceのテストから開始
/tdd handlePayment edge # handlePaymentのエッジケーステスト

TDDサイクル

code
┌─────────────────────────────────────────────────┐
│  1. RED    → テストを書く(失敗することを確認) │
│  2. GREEN  → 最小限のコードで通す               │
│  3. REFACTOR → リファクタ(テストは通ったまま)│
└─────────────────────────────────────────────────┘

ワークフロー

Step 1: RED(テストを書く)

  1. テスト対象を特定

    • 新機能: 期待する入出力を定義
    • バグ修正: バグを再現するテストを書く
  2. テストファイルを作成/更新

    typescript
    // tests/unit/services/user.service.test.ts
    describe('UserService', () => {
      describe('createUser', () => {
        it('should create user with valid data', async () => {
          const result = await userService.createUser({
            email: 'test@example.com',
            name: 'Test User',
          });
          expect(result.id).toBeDefined();
          expect(result.email).toBe('test@example.com');
        });
    
        it('should throw error for duplicate email', async () => {
          // エッジケース
          await expect(
            userService.createUser({ email: 'existing@example.com', name: 'Test' })
          ).rejects.toThrow('Email already exists');
        });
      });
    });
    
  3. テスト実行 → 失敗を確認

    bash
    npm run test -- --watch tests/unit/services/user.service.test.ts
    

Step 2: GREEN(実装する)

  1. 最小限の実装で通す

    • 完璧を目指さない
    • ハードコードでもOK(後でリファクタ)
    • テストが通ることだけに集中
  2. テスト実行 → 成功を確認

Step 3: REFACTOR(改善する)

  1. テストが通った状態で:

    • 重複を排除
    • 命名を改善
    • 構造を整理
  2. テスト実行 → まだ通ることを確認

  3. 満足したら次のテストへ(Step 1に戻る)


テストの書き方ガイド

命名規則

typescript
describe('[対象]', () => {
  describe('[メソッド/機能]', () => {
    it('should [期待する動作] when [条件]', () => {
      // ...
    });
  });
});

カバーすべきケース

ケース
正常系有効な入力で期待通り動作
境界値0, 空文字, 配列の最初/最後
エラー系無効な入力、null/undefined
エッジケース並行アクセス、タイムアウト

モックの使い方

typescript
// 外部依存はモック
vi.mock('@/db', () => ({
  db: {
    select: vi.fn().mockReturnValue({
      from: vi.fn().mockReturnValue({
        where: vi.fn().mockResolvedValue([{ id: '1', name: 'Test' }]),
      }),
    }),
  },
}));

テストコマンド例

Vitest

bash
npm run test                           # 全テスト
npm run test -- --watch               # ウォッチモード
npm run test -- tests/unit/specific   # 特定ディレクトリ
npm run test -- --coverage            # カバレッジ

Jest

bash
npm test                              # 全テスト
npm test -- --watch                   # ウォッチモード
npm test -- --coverage                # カバレッジ

Playwright (E2E)

bash
npm run test:e2e                      # 全E2E
npx playwright test --grep "login"    # 特定テスト

チェックリスト

TDD完了時の確認:

  • テストが先に書かれた(実装より前)
  • テストが一度失敗した(REDフェーズ)
  • 最小限の実装で通した(GREEN)
  • リファクタ後もテストが通る
  • エッジケースがカバーされている
  • テスト名が動作を説明している