tdd

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

/tdd - Test-Driven Development

/tdd - 测试驱动开发(TDD)

テスト駆動開発ワークフローを実行する。
用于执行测试驱动开发(TDD)工作流。

使い方

使用方法

/tdd                    # 対話的にTDDを開始
/tdd UserService        # UserServiceのテストから開始
/tdd handlePayment edge # handlePaymentのエッジケーステスト
/tdd                    # 以互动方式启动TDD
/tdd UserService        # 从编写UserService的测试开始
/tdd handlePayment edge # 编写handlePayment的边缘场景测试

TDDサイクル

TDD循环

┌─────────────────────────────────────────────────┐
│  1. RED    → テストを書く(失敗することを確認) │
│  2. GREEN  → 最小限のコードで通す               │
│  3. REFACTOR → リファクタ(テストは通ったまま)│
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│  1. RED    → 编写测试(确认测试失败) │
│  2. GREEN  → 用最少的代码让测试通过               │
│  3. REFACTOR → 重构(确保测试仍能通过)│
└─────────────────────────────────────────────────┘

ワークフロー

工作流

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

步骤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
  1. 确定测试对象
    • 新功能: 定义预期的输入输出
    • Bug修复: 编写能复现Bug的测试
  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(実装する)

步骤2:GREEN(实现功能)

  1. 最小限の実装で通す
    • 完璧を目指さない
    • ハードコードでもOK(後でリファクタ)
    • テストが通ることだけに集中
  2. テスト実行 → 成功を確認
  1. 最小化实现让测试通过
    • 不追求完美
    • 即使硬编码也可以(后续再重构)
    • 只专注于让测试通过
  2. 执行测试 → 确认测试成功

Step 3: REFACTOR(改善する)

步骤3:REFACTOR(代码重构)

  1. テストが通った状態で:
    • 重複を排除
    • 命名を改善
    • 構造を整理
  2. テスト実行 → まだ通ることを確認
  3. 満足したら次のテストへ(Step 1に戻る)

  1. 在测试通过的前提下:
    • 消除代码重复
    • 改善命名
    • 整理代码结构
  2. 执行测试 → 确认测试仍能通过
  3. 满意后进入下一个测试环节(回到步骤1)

テストの書き方ガイド

测试编写指南

命名規則

命名规则

typescript
describe('[対象]', () => {
  describe('[メソッド/機能]', () => {
    it('should [期待する動作] when [条件]', () => {
      // ...
    });
  });
});
typescript
describe('[测试对象]', () => {
  describe('[方法/功能]', () => {
    it('should [预期行为] when [条件]', () => {
      // ...
    });
  });
});

カバーすべきケース

需要覆盖的测试场景

ケース
正常系有効な入力で期待通り動作
境界値0, 空文字, 配列の最初/最後
エラー系無効な入力、null/undefined
エッジケース並行アクセス、タイムアウト
场景示例
正常场景有效输入下按预期运行
边界值0、空字符串、数组的首个/末尾元素
错误场景无效输入、null/undefined
边缘场景并发访问、超时

モックの使い方

Mock的使用方法

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

typescript
// 外部依赖使用Mock
vi.mock('@/db', () => ({
  db: {
    select: vi.fn().mockReturnValue({
      from: vi.fn().mockReturnValue({
        where: vi.fn().mockResolvedValue([{ id: '1', name: 'Test' }]),
      }),
    }),
  },
}));

テストコマンド例

测试命令示例

Vitest

Vitest

bash
npm run test                           # 全テスト
npm run test -- --watch               # ウォッチモード
npm run test -- tests/unit/specific   # 特定ディレクトリ
npm run test -- --coverage            # カバレッジ
bash
npm run test                           # 运行所有测试
npm run test -- --watch               # 监听模式
npm run test -- tests/unit/specific   # 运行指定目录的测试
npm run test -- --coverage            # 生成测试覆盖率报告

Jest

Jest

bash
npm test                              # 全テスト
npm test -- --watch                   # ウォッチモード
npm test -- --coverage                # カバレッジ
bash
npm test                              # 运行所有测试
npm test -- --watch                   # 监听模式
npm test -- --coverage                # 生成测试覆盖率报告

Playwright (E2E)

Playwright (E2E)

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

bash
npm run test:e2e                      # 运行所有E2E测试
npx playwright test --grep "login"    # 运行指定测试

チェックリスト

检查清单

TDD完了時の確認:
  • テストが先に書かれた(実装より前)
  • テストが一度失敗した(REDフェーズ)
  • 最小限の実装で通した(GREEN)
  • リファクタ後もテストが通る
  • エッジケースがカバーされている
  • テスト名が動作を説明している
TDD完成时的确认项:
  • 先编写测试(早于实现代码)
  • 测试曾失败过(RED阶段)
  • 用最小化实现让测试通过(GREEN阶段)
  • 重构后测试仍能通过
  • 已覆盖边缘场景
  • 测试名称能准确描述预期行为