SuperAPI AI Directory

tdd

Testing & quality

View repository

Updated 10-15 20:08

About this skill

/tdd - Test-Driven Development

Execute a test-driven development workflow.

How to use

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

TDD cycle

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

Workflow

Step 1: RED (write a test)

  1. Identify the test target

    • New feature: define the expected input/output
    • Bug fix: write a test that reproduces the bug
  2. Create/update the test file

    // 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. Run the test → confirm failure

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

Step 2: GREEN (implement)

  1. Minimal implementationto make it pass

    • Don't aim for perfection
    • Hardcoding is OK (refactor later)
    • Focus only on making the test pass
  2. Run the test → confirm success

Step 3: REFACTOR (improve)

  1. With the tests passing:

    • Eliminate duplication
    • Improve naming
    • Organize the structure
  2. Run the test → confirm it still passes

  3. When satisfied, move on to the next test (return to Step 1)


Guide to writing tests

Naming conventions

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

Cases to cover

Cases Example
Normal case Behaves as expected with valid input
Boundary values 0, empty string, first/last of an array
Error cases Invalid input, null/undefined
Edge cases Concurrent access, timeout

How to use mocks

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

Example test commands

Vitest

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

Jest

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

Playwright (E2E)

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

Checklist

Checklist when TDD is complete:

  • The test was written first (before the implementation)
  • The test failed once (RED phase)
  • It was made to pass with a minimal implementation (GREEN)
  • The test still passes after refactoring
  • Edge cases are covered
  • The test name describes the behavior