| name | test-driven-development |
| description | 实现任何功能或修复 bug 时,写实现代码之前必须使用。铁律:没有先看到失败的测试,就没有生产代码。触发词:写代码、实现功能、修bug、TDD、写测试。 |
| agent_created | true |
测试驱动开发(TDD)
核心原则
先写测试,看它失败,写最少代码让它通过。
如果你没看到测试失败,你就不知道它是否测试了正确的东西。
铁律
没有先看到失败的测试 = 没有生产代码
先写代码再写测试?删掉,重新来。
- 不要保留做"参考"
- 不要在写测试时"改编"它
- 不要看它
- 删除就是删除
何时使用
永远:
例外(需要人类伙伴确认):
"就这一次跳过 TDD 吧"?停。那是合理化借口。
红-绿-重构循环
RED — 写失败测试
写一个最小测试展示应该发生什么。
好的测试:
def test_retries_failed_operations_three_times():
attempts = 0
def operation():
nonlocal attempts
attempts += 1
if attempts < 3:
raise Exception("fail")
return "success"
result = retry(operation)
assert result == "success"
assert attempts == 3
清晰命名,测试真实行为,只测一件事
差的测试:
def test_retry():
mock = Mock(side_effect=[Exception, Exception, "success"])
retry(mock)
assert mock.call_count == 3
模糊命名,测 mock 不是测代码
要求:
- 一个行为一个测试
- 清晰的描述性名称
- 测试真实代码(非必要不用 mock)
验证 RED — 看它失败
强制步骤,不可跳过。
pytest tests/path/test.py::test_name -v
确认:
- 测试失败(不是报错)
- 失败信息符合预期
- 失败原因 = 功能缺失,不是拼写错误
测试直接通过? 你在测已有行为,修正测试。
GREEN — 最少代码
写最简单的代码让测试通过。不要:
- 加测试不需要的选项
- 重构其他代码
- "改进"超过测试要求的范围
验证 GREEN — 看它通过
强制步骤。
pytest tests/path/test.py::test_name -v
确认:
- 目标测试通过
- 所有其他测试仍然通过
- 输出干净(无错误、无警告)
REFACTOR — 清理
只在绿色之后:
保持测试绿色,不要添加行为。
重复
下一个失败测试 → 下一个功能。
好测试的标准
| 品质 | 好的 | 差的 |
|---|
| 最小 | 一件事。名字里有"和"?拆开。 | test_validates_email_and_domain() |
| 清晰 | 名字描述行为 | test1、test_works |
| 展示意图 | 展示期望的 API | 隐藏代码应该做什么 |
为什么顺序重要
"我先写代码再补测试来验证"
事后写的测试立刻通过。立刻通过什么也证明不了:
- 可能测了错的东西
- 可能测了实现而非行为
- 可能漏了边界情况
- 你从来没见过它抓到 bug
常见借口粉碎
| 借口 | 现实 |
|---|
| "太简单不用测" | 简单代码也会坏。写测试只花 30 秒。 |
| "我稍后补测试" | 测试立即通过证明不了任何事。 |
| "我已经手动测过了" | 临时的 ≠ 系统性的。没有记录,无法重跑。 |
| "删掉 X 小时工作太浪费" | 沉没成本谬误。保留未验证代码 = 技术债。 |
| "TDD 太死板,实际点" | TDD 就是实际的:提交前找到 bug(比事后调试快)。 |
| "保留做参考,先写测试" | 你会改编它。那就是测试后补。删除就是删除。 |
红灯 — 停,重来
以下任何情况出现,意味着跳过 TDD:
- 测试之前写了代码
- 代码后补的测试
- 测试立即通过
- 解释不了测试为什么失败
- "我稍后加测试"
- 合理化"就这一次"
- "我已经手动测过了"
以上全部意味着:删代码,用 TDD 重来。
验证清单
完成前检查: