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)
-
Identify the test target
- New feature: define the expected input/output
- Bug fix: write a test that reproduces the bug
-
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'); }); }); }); -
Run the test → confirm failure
npm run test -- --watch tests/unit/services/user.service.test.ts
Step 2: GREEN (implement)
-
Minimal implementationto make it pass
- Don't aim for perfection
- Hardcoding is OK (refactor later)
- Focus only on making the test pass
-
Run the test → confirm success
Step 3: REFACTOR (improve)
-
With the tests passing:
- Eliminate duplication
- Improve naming
- Organize the structure
-
Run the test → confirm it still passes
-
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