tdd-workflow
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTDD 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 or
pytest-patternsfor HOW to write tests)react-testing-patterns
在以下场景激活此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(红)—— 编写一个失败的测试
- Write exactly ONE test that describes the expected behavior
- The test must be specific: test one behavior, not multiple
- Run the test suite and confirm the new test FAILS
- 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- 编写恰好一个描述预期行为的测试
- 测试必须具体:仅测试一个行为,而非多个
- 运行测试套件,确认新测试失败
- 失败信息应清晰描述缺失的内容
后端(pytest):
bash
pytest tests/unit/test_user_service.py::test_create_user_returns_user -xExpected: 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=verboseExpected: 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(绿)—— 使测试通过
- Write the MINIMUM code to make the failing test pass
- Do NOT add extra functionality, error handling, or edge cases
- It's okay to hardcode values or use simple implementations
- Run the test suite and confirm ALL tests pass (not just the new one)
bash
undefined- 编写最少代码使失败的测试通过
- 不要添加额外功能、错误处理或边缘情况
- 可以硬编码值或使用简单实现
- 运行测试套件,确认所有测试通过(不只是新测试)
bash
undefinedBackend
后端
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 REFACTORnpx vitest run src/hooks/useAuth.test.ts
**GREEN阶段规则:**
- 最少代码就是真正的最少——如果常量能满足测试,就使用常量
- 不要添加任何测试未要求的代码
- 此阶段不要进行重构
- 此阶段不要编写额外测试
- 如果测试通过,进入REFACTOR阶段Phase 3: REFACTOR — Clean Up
阶段3:REFACTOR(重构)—— 代码清理
- Improve code quality while keeping all tests green
- Remove duplication (DRY)
- Improve naming and readability
- Extract functions or classes if needed
- Run tests after EVERY change — they must stay green
bash
undefined- 在保持所有测试通过的前提下提升代码质量
- 消除重复代码(遵循DRY原则)
- 优化命名和可读性
- 如有需要,提取函数或类
- 每次修改后运行测试——必须保持全部通过
bash
undefinedAfter 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 immediatelypytest tests/ -x # 必须通过
npx vitest run # 必须通过
**REFACTOR阶段规则:**
- 每次修改必须保持测试通过
- 同时重构生产代码和测试代码
- 不要添加新功能(新功能需要进入新的RED阶段)
- 如果测试失败,立即撤销重构修改Phase 4: COMMIT
阶段4:提交代码
After a successful REFACTOR phase:
- Stage all changes (test + implementation)
- Commit with a descriptive message
- Return to Phase 1 (RED) for the next behavior
成功完成REFACTOR阶段后:
- 暂存所有修改(测试+实现代码)
- 用描述性信息提交代码
- 返回阶段1(RED)处理下一个行为
Strict TDD Rules
严格TDD规则
These rules are non-negotiable when this skill is active:
- NEVER write production code without a failing test first
- NEVER write more than one failing test at a time
- NEVER add functionality that no test requires
- ALWAYS run tests after every change
- ALWAYS commit after each successful GREEN-REFACTOR cycle
- ALWAYS keep the RED-GREEN-REFACTOR cycle short (minutes, not hours)
当此Skill激活时,以下规则不可违反:
- 绝对不要在没有失败测试的情况下编写生产代码
- 绝对不要同时编写多个失败的测试
- 绝对不要添加任何测试未要求的功能
- 每次修改后必须运行测试
- 每次成功完成GREEN-REFACTOR循环后必须提交代码
- 保持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_email1. 编写测试: 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_emailFrontend 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 clicked1. 编写测试: 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 clickedBug 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 NoneGREEN: Implement with basic logic.
REFACTOR: Extract password hashing. Commit.
UserService.create_userCycle 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 NoneGREEN: 实现的基础逻辑。
REFACTOR: 提取密码哈希逻辑。提交代码。
UserService.create_user循环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), static content, auto-generated code (Alembic migrations), one-off scripts, and exploratory prototyping.tsconfig.json -
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). Useor
pytest-patternsfor the PATTERNS (how to structure tests, which assertions to use).react-testing-patterns
-
何时跳过TDD: 配置文件(、
.env)、静态内容、自动生成的代码(Alembic迁移)、一次性脚本和探索性原型开发。tsconfig.json -
带有外部依赖的TDD: 在边界处进行Mock。如果测试调用外部API的服务,Mock API客户端而非HTTP库。测试服务的行为,而非Mock本身。
-
大型功能: 将功能拆分为小的、可测试的行为。每个行为对应一个RED-GREEN-REFACTOR循环。所有循环的总和实现完整功能。
-
为无测试的现有代码重构: 先为现有行为编写测试(特征测试)。然后以这些测试为安全网进行重构。这不是严格的TDD,但属于测试优先思维的有效应用。
-
与模式类Skill配合使用: 此Skill定义工作流(何时编写测试 vs 代码)。使用或
pytest-patterns了解测试模式(如何构建测试、使用哪些断言)。react-testing-patterns