tdd-workflow

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TDD Workflow

TDD 工作流

When to Use

使用场景

Activate this skill when:
  • The user explicitly requests TDD, test-first, or red-green-refactor
  • Implementing new functions, methods, endpoints, or components where test-first is valuable
  • Fixing bugs where a regression test should be written first
  • The user says "write the test first", "TDD this", or "red-green-refactor"
Do NOT use this skill for:
  • Configuration files, environment setup, or static content
  • One-line fixes or trivial changes
  • Exploratory prototyping or proof-of-concept code
  • Code that cannot be meaningfully tested in isolation
  • Testing pattern details (use
    pytest-patterns
    or
    react-testing-patterns
    for HOW to write tests)
在以下场景激活此Skill:
  • 用户明确请求TDD、测试优先或红-绿-重构
  • 实现新函数、方法、接口或组件时,测试优先的方式更有价值
  • 修复Bug时,应先编写回归测试
  • 用户提到“先写测试”、“用TDD实现这个”或“红-绿-重构”
请勿在以下场景使用此Skill:
  • 配置文件、环境搭建或静态内容
  • 单行修复或微小改动
  • 探索性原型开发或概念验证代码
  • 无法独立进行有意义测试的代码
  • 测试模式细节(如需了解如何编写测试,请使用
    pytest-patterns
    react-testing-patterns

Instructions

操作说明

The TDD Cycle

TDD 循环

┌─────────────────────────────────────────────────────┐
│                                                     │
│   ┌─────┐     ┌───────┐     ┌──────────┐          │
│   │ RED │ ──→ │ GREEN │ ──→ │ REFACTOR │ ──→ ...  │
│   └─────┘     └───────┘     └──────────┘          │
│                                                     │
│   Write ONE    Write MINIMUM   Clean up code        │
│   failing      code to make    while ALL tests      │
│   test         it pass         stay GREEN            │
│                                                     │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│                                                     │
│   ┌─────┐     ┌───────┐     ┌──────────┐          │
│   │ RED │ ──→ │ GREEN │ ──→ │ REFACTOR │ ──→ ...  │
│   └─────┘     └───────┘     └──────────┘          │
│                                                     │
│   编写一个    编写最少   清理代码                     │
│   失败的测试  代码使测试  保持所有测试                │
│               通过       处于GREEN状态                │
│                                                     │
└─────────────────────────────────────────────────────┘

Phase 1: RED — Write a Failing Test

阶段1:RED(红)—— 编写一个失败的测试

  1. Write exactly ONE test that describes the expected behavior
  2. The test must be specific: test one behavior, not multiple
  3. Run the test suite and confirm the new test FAILS
  4. The failure message should clearly describe what is missing
Backend (pytest):
bash
pytest tests/unit/test_user_service.py::test_create_user_returns_user -x
  1. 编写恰好一个描述预期行为的测试
  2. 测试必须具体:仅测试一个行为,而非多个
  3. 运行测试套件,确认新测试失败
  4. 失败信息应清晰描述缺失的内容
后端(pytest):
bash
pytest tests/unit/test_user_service.py::test_create_user_returns_user -x

Expected: FAILED (function/class does not exist yet)

预期结果:失败(函数/类尚未存在)


**Frontend (Vitest):**
```bash
npx vitest run src/hooks/useAuth.test.ts --reporter=verbose

**前端(Vitest):**
```bash
npx vitest run src/hooks/useAuth.test.ts --reporter=verbose

Expected: FAILED (hook/component does not exist yet)

预期结果:失败(钩子/组件尚未存在)


**Rules for RED phase:**
- Write the simplest test that expresses the requirement
- The test should fail for the RIGHT reason (missing implementation, not syntax error)
- Don't write more than one failing test at a time
- Import from the location where the code WILL live (even though it doesn't exist yet)

**RED阶段规则:**
- 编写能表达需求的最简单测试
- 测试应因正确的原因失败(缺少实现,而非语法错误)
- 不要同时编写多个失败的测试
- 从代码最终存放的位置导入(即使代码尚未存在)

Phase 2: GREEN — Make It Pass

阶段2:GREEN(绿)—— 使测试通过

  1. Write the MINIMUM code to make the failing test pass
  2. Do NOT add extra functionality, error handling, or edge cases
  3. It's okay to hardcode values or use simple implementations
  4. Run the test suite and confirm ALL tests pass (not just the new one)
bash
undefined
  1. 编写最少代码使失败的测试通过
  2. 不要添加额外功能、错误处理或边缘情况
  3. 可以硬编码值或使用简单实现
  4. 运行测试套件,确认所有测试通过(不只是新测试)
bash
undefined

Backend

后端

pytest tests/unit/test_user_service.py -x
pytest tests/unit/test_user_service.py -x

Frontend

前端

npx vitest run src/hooks/useAuth.test.ts

**Rules for GREEN phase:**
- Minimum code means minimum — if a constant satisfies the test, use a constant
- Do not add code that no test requires
- Do not refactor during this phase
- Do not write additional tests during this phase
- If tests pass, move to REFACTOR
npx vitest run src/hooks/useAuth.test.ts

**GREEN阶段规则:**
- 最少代码就是真正的最少——如果常量能满足测试,就使用常量
- 不要添加任何测试未要求的代码
- 此阶段不要进行重构
- 此阶段不要编写额外测试
- 如果测试通过,进入REFACTOR阶段

Phase 3: REFACTOR — Clean Up

阶段3:REFACTOR(重构)—— 代码清理

  1. Improve code quality while keeping all tests green
  2. Remove duplication (DRY)
  3. Improve naming and readability
  4. Extract functions or classes if needed
  5. Run tests after EVERY change — they must stay green
bash
undefined
  1. 在保持所有测试通过的前提下提升代码质量
  2. 消除重复代码(遵循DRY原则)
  3. 优化命名和可读性
  4. 如有需要,提取函数或类
  5. 每次修改后运行测试——必须保持全部通过
bash
undefined

After each refactoring change

每次重构修改后

pytest tests/ -x # Must pass npx vitest run # Must pass

**Rules for REFACTOR phase:**
- Every change must keep tests green
- Refactor both production code AND test code
- Do NOT add new functionality (that requires a new RED phase)
- If you break a test, undo the refactoring immediately
pytest tests/ -x # 必须通过 npx vitest run # 必须通过

**REFACTOR阶段规则:**
- 每次修改必须保持测试通过
- 同时重构生产代码和测试代码
- 不要添加新功能(新功能需要进入新的RED阶段)
- 如果测试失败,立即撤销重构修改

Phase 4: COMMIT

阶段4:提交代码

After a successful REFACTOR phase:
  1. Stage all changes (test + implementation)
  2. Commit with a descriptive message
  3. Return to Phase 1 (RED) for the next behavior
成功完成REFACTOR阶段后:
  1. 暂存所有修改(测试+实现代码)
  2. 用描述性信息提交代码
  3. 返回阶段1(RED)处理下一个行为

Strict TDD Rules

严格TDD规则

These rules are non-negotiable when this skill is active:
  1. NEVER write production code without a failing test first
  2. NEVER write more than one failing test at a time
  3. NEVER add functionality that no test requires
  4. ALWAYS run tests after every change
  5. ALWAYS commit after each successful GREEN-REFACTOR cycle
  6. ALWAYS keep the RED-GREEN-REFACTOR cycle short (minutes, not hours)
当此Skill激活时,以下规则不可违反:
  1. 绝对不要在没有失败测试的情况下编写生产代码
  2. 绝对不要同时编写多个失败的测试
  3. 绝对不要添加任何测试未要求的功能
  4. 每次修改后必须运行测试
  5. 每次成功完成GREEN-REFACTOR循环后必须提交代码
  6. 保持RED-GREEN-REFACTOR循环简短(以分钟为单位,而非小时)

Backend TDD Flow (pytest)

后端TDD流程(pytest)

1. Write test:     tests/unit/test_user_service.py::test_create_user_returns_user
2. Run:            pytest tests/unit/test_user_service.py::test_create_user_returns_user -x
3. See:            FAILED - ImportError or AssertionError
4. Implement:      app/services/user_service.py (minimum code)
5. Run:            pytest tests/unit/test_user_service.py -x
6. See:            PASSED
7. Refactor:       Clean up, run tests again
8. Commit:         "Add UserService.create_user"
9. Next test:      test_create_user_rejects_duplicate_email
1. 编写测试:     tests/unit/test_user_service.py::test_create_user_returns_user
2. 运行:            pytest tests/unit/test_user_service.py::test_create_user_returns_user -x
3. 结果:            失败 - 导入错误或断言错误
4. 实现:      app/services/user_service.py(最少代码)
5. 运行:            pytest tests/unit/test_user_service.py -x
6. 结果:            通过
7. 重构:       清理代码,再次运行测试
8. 提交:         "添加UserService.create_user"
9. 下一个测试:      test_create_user_rejects_duplicate_email

Frontend TDD Flow (Testing Library)

前端TDD流程(Testing Library)

1. Write test:     src/components/UserCard.test.tsx::renders user name
2. Run:            npx vitest run src/components/UserCard.test.tsx
3. See:            FAILED - module not found
4. Implement:      src/components/UserCard.tsx (minimum code)
5. Run:            npx vitest run src/components/UserCard.test.tsx
6. See:            PASSED
7. Refactor:       Clean up, run tests again
8. Commit:         "Add UserCard component"
9. Next test:      calls onEdit when button clicked
1. 编写测试:     src/components/UserCard.test.tsx::renders user name
2. 运行:            npx vitest run src/components/UserCard.test.tsx
3. 结果:            失败 - 模块未找到
4. 实现:      src/components/UserCard.tsx(最少代码)
5. 运行:            npx vitest run src/components/UserCard.test.tsx
6. 结果:            通过
7. 重构:       清理代码,再次运行测试
8. 提交:         "添加UserCard组件"
9. 下一个测试:      calls onEdit when button clicked

Bug Fix TDD Flow

Bug修复TDD流程

When fixing a bug, always start with a failing test that reproduces the bug:
1. Reproduce:      Understand the bug and its trigger condition
2. Write test:     Test that exercises the exact scenario that causes the bug
3. Run:            Confirm FAILED (the test reproduces the bug)
4. Fix:            Implement the minimum fix
5. Run:            Confirm PASSED (bug is fixed)
6. Refactor:       Clean up if needed
7. Commit:         "Fix: [describe the bug]"
This guarantees the bug cannot regress — the test will catch it.
修复Bug时,始终先编写一个能复现Bug的失败测试:
1. 复现Bug:      理解Bug及其触发条件
2. 编写测试:     测试能触发Bug的具体场景
3. 运行:            确认测试失败(测试复现了Bug)
4. 修复:            实现最少修复代码
5. 运行:            确认测试通过(Bug已修复)
6. 重构:       如有需要,清理代码
7. 提交:         "修复:[描述Bug内容]"
这能确保Bug不会再次出现——测试会捕获回归问题。

Examples

示例

TDD: UserService.create_user (3 Cycles)

TDD:UserService.create_user(3个循环)

Cycle 1 — RED: Test that create_user returns a user
python
async def test_create_user_returns_user(db_session):
    service = UserService(db_session)
    user = await service.create_user(UserCreate(email="a@b.com", password="12345678", display_name="A"))
    assert user.email == "a@b.com"
    assert user.id is not None
GREEN: Implement
UserService.create_user
with basic logic. REFACTOR: Extract password hashing. Commit.
Cycle 2 — RED: Test that duplicate email raises error
python
async def test_create_user_rejects_duplicate_email(db_session):
    service = UserService(db_session)
    await service.create_user(UserCreate(email="a@b.com", password="12345678", display_name="A"))
    with pytest.raises(ConflictError):
        await service.create_user(UserCreate(email="a@b.com", password="87654321", display_name="B"))
GREEN: Add duplicate check before insert. REFACTOR: Clean up. Commit.
Cycle 3 — RED: Test that password is hashed
python
async def test_create_user_hashes_password(db_session):
    service = UserService(db_session)
    user = await service.create_user(UserCreate(email="a@b.com", password="12345678", display_name="A"))
    assert user.hashed_password != "12345678"
    assert verify_password("12345678", user.hashed_password)
GREEN: Already passing from cycle 1 refactor? Then this test is a verification, not a RED. Write a test for a NEW behavior instead.
循环1 — RED: 测试create_user返回用户对象
python
async def test_create_user_returns_user(db_session):
    service = UserService(db_session)
    user = await service.create_user(UserCreate(email="a@b.com", password="12345678", display_name="A"))
    assert user.email == "a@b.com"
    assert user.id is not None
GREEN: 实现
UserService.create_user
的基础逻辑。 REFACTOR: 提取密码哈希逻辑。提交代码。
循环2 — RED: 测试重复邮箱会抛出错误
python
async def test_create_user_rejects_duplicate_email(db_session):
    service = UserService(db_session)
    await service.create_user(UserCreate(email="a@b.com", password="12345678", display_name="A"))
    with pytest.raises(ConflictError):
        await service.create_user(UserCreate(email="a@b.com", password="87654321", display_name="B"))
GREEN: 在插入前添加重复检查。 REFACTOR: 清理代码。提交代码。
循环3 — RED: 测试密码已被哈希
python
async def test_create_user_hashes_password(db_session):
    service = UserService(db_session)
    user = await service.create_user(UserCreate(email="a@b.com", password="12345678", display_name="A"))
    assert user.hashed_password != "12345678"
    assert verify_password("12345678", user.hashed_password)
GREEN: 如果循环1的重构后已经通过?那这个测试是验证性测试,不属于RED阶段。应该编写针对新行为的测试。

Edge Cases

边缘情况

  • When to skip TDD: Configuration files (
    .env
    ,
    tsconfig.json
    ), static content, auto-generated code (Alembic migrations), one-off scripts, and exploratory prototyping.
  • TDD with external dependencies: Mock at the boundary. If testing a service that calls an external API, mock the API client, not the HTTP library. Test the service's behavior, not the mock.
  • Large features: Break the feature into small, testable behaviors. Each behavior gets its own RED-GREEN-REFACTOR cycle. The sum of all cycles implements the full feature.
  • Refactoring existing code without tests: First write tests for the existing behavior (characterization tests). Then refactor with those tests as a safety net. This is not strict TDD but is a valid use of the test-first mindset.
  • Pair with pattern skills: This skill defines the WORKFLOW (when to write tests vs code). Use
    pytest-patterns
    or
    react-testing-patterns
    for the PATTERNS (how to structure tests, which assertions to use).
  • 何时跳过TDD: 配置文件(
    .env
    tsconfig.json
    )、静态内容、自动生成的代码(Alembic迁移)、一次性脚本和探索性原型开发。
  • 带有外部依赖的TDD: 在边界处进行Mock。如果测试调用外部API的服务,Mock API客户端而非HTTP库。测试服务的行为,而非Mock本身。
  • 大型功能: 将功能拆分为小的、可测试的行为。每个行为对应一个RED-GREEN-REFACTOR循环。所有循环的总和实现完整功能。
  • 为无测试的现有代码重构: 先为现有行为编写测试(特征测试)。然后以这些测试为安全网进行重构。这不是严格的TDD,但属于测试优先思维的有效应用。
  • 与模式类Skill配合使用: 此Skill定义工作流(何时编写测试 vs 代码)。使用
    pytest-patterns
    react-testing-patterns
    了解测试模式(如何构建测试、使用哪些断言)。