关于 tdd
tdd 是强制先测试再开发的技能,要求测试先失败,再编写最少实现使其通过。它还规定了规划、编写测试、实现和验证的 Agent 顺序。
主要功能
- 01生产代码前先编写失败测试
- 02Red-Green-Refactor 工作流
- 03依据测试编写最少实现
- 04用于规划和验证的 Agent 顺序
- 05测试与套件验证清单
适用场景
- 01以测试优先方式实现功能
- 02为 Bug 修复添加回归测试
- 03按 TDD 阶段协调 Agent
- 04完成前验证新旧测试
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ parcadei/ continuous-claude-v3 --skill tdd安装后在 Claude Code 中输入/tdd确认已加载。
详细介绍
/tdd - 测试驱动开发工作流
严格遵循 TDD 工作流:先写测试,再实现。
适用场景
- “使用 TDD 实现 X”
- “先写测试,再构建此功能”
- “先为 X 编写测试,再实现”
- 任何测试覆盖至关重要的功能
- 需要回归测试的 Bug 修复
TDD 理念
概述
先写测试。观察测试失败。编写最少的代码使其通过。
核心原则: 如果你没有观察到测试失败,就不知道它是否测试了正确的内容。
违反规则的字面要求,就是违背规则的本意。
铁律
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
先写了代码再写测试?删除它,重新开始。
没有例外:
- 不要把它留作“参考”
- 写测试时不要“调整”它
- 不要查看它
- 删除就是删除
根据测试重新实现。就是这样。
Red-Green-Refactor
RED - 编写失败的测试
编写一个最简测试,说明应该发生什么。
正确示例:
test('retries failed operations 3 times', async () => {
let attempts = 0;
const operation = () => {
attempts++;
if (attempts < 3) throw new Error('fail');
return 'success';
};
const result = await retryOperation(operation);
expect(result).toBe('success');
expect(attempts).toBe(3);
});
名称清晰、测试真实行为、只测试一件事。
错误示例:
test('retry works', async () => {
const mock = jest.fn()
.mockRejectedValueOnce(new Error())
.mockResolvedValueOnce('success');
await retryOperation(mock);
expect(mock).toHaveBeenCalledTimes(3);
});
名称含糊、测试的是模拟对象而非代码。
要求:
- 一个行为
- 名称清晰
- 真实代码(除非无法避免,否则不使用 Mock)
验证 RED - 观察它失败
必须执行。绝不跳过。
npm test path/to/test.test.ts
# or
pytest path/to/test_file.py
确认:
- 测试失败(而非报错)
- 失败消息符合预期
- 失败原因是功能缺失(而非拼写错误)
测试通过了? 你测试的是已有行为。修正测试。
测试报错了? 修复错误,重新运行,直到它正确地失败。
GREEN - 编写最少的代码
编写能通过测试的最简单代码。
正确示例:
async function retryOperation<T>(fn: () => Promise<T>): Promise<T> {
for (let i = 0; i < 3; i++) {
try {
return await fn();
} catch (e) {
if (i === 2) throw e;
}
}
throw new Error('unreachable');
}
刚好足以通过测试。
错误示例:
async function retryOperation<T>(
fn: () => Promise<T>,
options?: {
maxRetries?: number;
backoff?: 'linear' | 'exponential';
onRetry?: (attempt: number) => void;
}
): Promise<T> {
// YAGNI - over-engineered
}
不要添加功能、重构其他代码,也不要超出测试要求进行“改进”。
验证 GREEN - 观察它通过
必须执行。
npm test path/to/test.test.ts
确认:
- 测试通过
- 其他测试仍然通过
- 输出干净(没有错误或警告)
测试失败了? 修复代码,不要改测试。
其他测试失败了? 立即修复。
REFACTOR - 清理代码
只有通过测试后才能进行:
- 消除重复
- 改进命名
- 提取辅助函数
确保测试持续通过。不要添加行为。
常见借口
| 借口 | 事实 |
|---|---|
| “太简单,不值得测试” | 简单代码也会出错。测试只需 30 秒。 |
| “之后再测试” | 测试一开始就通过,说明不了任何问题。 |
| “之后测试也能达到同样目的” | 先写测试 =“这应该做什么?”之后写测试 =“这做了什么?” |
| “我已经手动测试过了” | 临时测试 ≠ 系统测试。没有记录,也无法重跑。 |
| “删除数小时的工作太浪费了” | 沉没成本谬误。保留未经验证的代码就是技术债务。 |
| “留作参考,先写测试” | 你会据此调整代码。这就是之后写测试。删除就是删除。 |
| “需要先探索一番” | 可以。丢弃探索结果,从 TDD 开始。 |
| “测试很难 = 设计不清晰” | 听听测试的反馈。难以测试 = 难以使用。 |
| “TDD 会拖慢我” | TDD 比调试更快。务实的做法 = 先写测试。 |
| “手动测试更快” | 手动测试无法证明边界情况也能正常工作。每次变更后你都得重新测试。 |
危险信号 - 停下并重新开始
- 先写代码再写测试
- 实现后才写测试
- 测试一开始就通过
- 无法解释测试为何失败
- 测试被“留到以后”再添加
- 找借口说“这次就破例”
- “我已经手动测试过了”
- “之后测试也能达到同样目的”
- “留作参考”或“调整现有代码”
上述任何情况都意味着:删除代码。从 TDD 重新开始。
验证清单
标记工作完成前:
- 每个新增函数/方法都有测试
- 实现前观察了每个测试失败
- 每个测试都因预期原因失败(功能缺失,而非拼写错误)
- 编写了通过每个测试所需的最少代码
- 所有测试均通过
- 输出干净(没有错误或警告)
- 测试使用真实代码(只有无法避免时才使用 Mock)
- 覆盖边界情况和错误
无法勾选所有项目?说明你跳过了 TDD。重新开始。
遇到困难时
| 问题 | 解决方案 |
|---|---|
| 不知道如何测试 | 编写期望的 API。先写断言。向你的协作伙伴请教。 |
| 测试过于复杂 | 设计过于复杂。简化接口。 |
| 必须模拟所有内容 | 代码耦合过于紧密。使用依赖注入。 |
| 测试设置过于庞大 | 提取辅助函数。如果仍然复杂?简化设计。 |
工作流执行
工作流概览
┌────────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐
│ plan- │───▶│ arbiter │───▶│ kraken │───▶│ arbiter │
│ agent │ │ │ │ │ │ │
└────────────┘ └──────────┘ └──────────┘ └───────────┘
Design Write Implement Verify
approach failing minimal all tests
tests code pass
Agent 序列
| # | Agent | 角色 | 输出 |
|---|---|---|---|
| 1 | plan-agent | 设计测试用例和实现方案 | 测试计划 |
| 2 | arbiter | 编写失败的测试(RED 阶段) | 测试文件 |
| 3 | kraken | 实现通过测试所需的最少代码(GREEN 阶段) | 实现 |
| 4 | arbiter | 运行所有测试,确认没有破坏任何内容 | 测试报告 |
核心原则
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
每个 Agent 都遵循 TDD 合约:
- arbiter 编写的测试起初必须失败
- kraken 编写最少的代码来让测试通过
- arbiter 确认整个测试套件通过
执行
阶段 1:规划测试用例
Task(
subagent_type="plan-agent",
prompt="""
Design TDD approach for: [FEATURE_NAME]
Define:
1. What behaviors need to be tested
2. Edge cases to cover
3. Expected test structure
DO NOT write any implementation code.
Output: Test plan document
"""
)
阶段 2:编写失败的测试(RED)
Task(
subagent_type="arbiter",
prompt="""
Write failing tests for: [FEATURE_NAME]
Test plan: [from phase 1]
Requirements:
- Write tests FIRST
- Run tests to confirm they FAIL
- Tests must fail because feature is missing (not syntax errors)
- Create clear test names describing expected behavior
DO NOT write any implementation code.
"""
)
阶段 3:实现(GREEN)
Task(
subagent_type="kraken",
prompt="""
Implement MINIMAL code to pass tests: [FEATURE_NAME]
Tests location: [test file path]
Requirements:
- Write ONLY enough code to make tests pass
- No additional features beyond what tests require
- No "improvements" or "enhancements"
- Run tests after each change
Follow Red-Green-Refactor strictly.
"""
)
阶段 4:验证
Task(
subagent_type="arbiter",
prompt="""
Validate TDD implementation: [FEATURE_NAME]
- Run full test suite
- Verify all new tests pass
- Verify no existing tests broke
- Check test coverage if available
"""
)
强制执行的 TDD 规则
- arbiter 不能编写实现代码
- kraken 不能添加未经测试的功能
- 实现前测试必须失败
- 实现后测试必须通过
示例
User: /tdd Add email validation to the signup form
Claude: Starting /tdd workflow for email validation...
Phase 1: Planning test cases...
[Spawns plan-agent]
Test plan:
- Valid email formats
- Invalid email formats
- Empty email rejection
- Edge cases (unicode, long emails)
Phase 2: Writing failing tests (RED)...
[Spawns arbiter]
✅ 8 tests written, all failing as expected
Phase 3: Implementing minimal code (GREEN)...
[Spawns kraken]
✅ All 8 tests now passing
Phase 4: Validating...
[Spawns arbiter]
✅ 247 tests passing (8 new), 0 failing
TDD workflow complete!
重构阶段(可选)
GREEN 阶段之后,你可以添加一个重构阶段:
Task(
subagent_type="kraken",
prompt="""
Refactor: [FEATURE_NAME]
- Clean up code while keeping tests green
- Remove duplication
- Improve naming
- Extract helpers if needed
DO NOT add new behavior. Keep all tests passing.
"""
)
评论
支持 Markdown