本节摘要:TDD 主张"先写测试让它红,再写最小代码让它绿,最后重构",用红灯牵引设计而非事后补测。本节把红-绿-重构循环讲透,并给出一个可运行的最小循环示例,以及它成立的三个前提。
直觉上,测试是"写完功能再补"的。但 TDD 把顺序倒过来——先写一个会失败的测试,逼你把"我到底想要什么行为"想清楚,再让代码恰好让它绿。倒过来的好处不是玄学,而是逼你在写实现前先定契约:接口长什么样、输入输什么、边界在哪。很多"写完再补测试"的项目里,那些测试不过是在给已经写死、难改的实现盖个章,而 TDD 让测试成为塑造代码形状的模具。
这个循环很短,但每一步都有讲究,缺一不可。
第一步,红:写一个失败测试。它先失败,因为功能还不存在。这一步的关键是"最小且明确"——只测一个行为点,别一上来写个大而全的套件。
第二步,绿:写最少量的代码让测试通过。别偷懒越过测试直接上完美实现,宁可笨拙地先让它绿,绿灯是你的安全网。
第三步,重构:保持绿灯的前提下,把代码和测试都收拾干净——消重复、剔死代码、提可读。测试是你的安全网,所以重构才敢放手。
下面把循环画出来,你跟着它走就不容易乱。

用一个纯逻辑例子演示,Python + pytest。
先写红灯:
def test_should_reject_amount_less_than_zero(): assert not is_valid_order_amount(-1)
(此时 is_valid_order_amount 未定义,必然红。)
再写最小实现,让绿:
def is_valid_order_amount(amount): return amount > 0
(现在测试绿了。)最后重构:把实现和测试都收拾一下,比如补充第二个边界 should_accept_zero 并确认绿灯。整个循环几分钟内完成,你在设计、实现、验证之间反复获得即时反馈。
TDD 不是拿来就灵,它的见效依赖三个前提,缺一个都会退化成"形式红绿"。
前提一:被测代码能隔离。如果代码内部直接 new 数据库、硬编码网络,你写不出"先红"的测试——因为外部依赖太吵。所以 TDD 反过来倒逼你把依赖设计成可注入的(回看第 2.2 节),这是一环扣一环的。
前提二:需求能拆成行为点。若需求含糊到"谁知道该返回什么",就没法定出红的第一条。TDD 适合能画出输入输出契约的功能,含糊的需求先写清楚再循环。
前提三:团队愿意保持绿灯。重构阶段一旦红灯长期挂起,人就会开始绕过测试走捷径。灯绿不绿、常常跑,是循环能不能转下去的前提。
TDD 的高光场景是核心逻辑、算价、状态机这种"契约清晰 + 高频改动"的地方。它不适合所有场景:简单的胶水代码、一次性脚本、纯探索性原型,硬上 TDD 反而拖慢节奏。判断的口径是这里有没有值得守护的回归风险——有,就让测试先行;没有,别为仪式感牺牲速度。
很多人第一次跑 TDD,红灯是红了,但红得没有意义——不是因为"这个行为还没实现",而是因为"断言写错"或"函数压根没写对名字"。要区分"我还没写功能"的红 与"我写错测试"的红,有一个自查:把实现换成你认为的最终答案,如果灯还是红的,就说明问题出在测试本身而不是功能。TDD 的红必须"可预期地由渐变绿",一旦红灯的来源说不清,就是在给循环埋雷。
对照身边常见的消耗:有人停在一堆假红里出不来,因为一次塞了太多行为点;有人为了图快,用"断言非空""断言不抛"这种恒真断言混过关,那叫假绿不叫真绿(回看 2.5)。真正合格的红,是"换对实现必绿、改掉实现必红",这两端都对得齐,循环才值得托付。
TDD 最容易被讲成一味"快",但快有快的前提,否则会掉进"重写陷阱":一上来就想写出宏大设计,结果大步子跨太快、绿灯半天不亮,反而泄气。成熟的节奏反而是"小步"——每次只让一条断言变绿,跑一圈,绿了再迈下一步。尽管单步很小,但每一步都有即时反馈,心理负担最轻。更细的握法是把"步"拆成一红、一绿、一重构三小格,宁可多转两圈,也不要一口吞一个大改动。
现实中真正拖垮 TDD 的,往往不是步子太小,而是重构阶段被无视。很多团队只做"红-绿",把"重构"当可选项跳过,钩起的死代码、重复逻辑越积越多,测试照样绿,但代码的形状在悄悄变坏。别忘了三步循环里,重构才是让绿灯长期有含金量的那一步。
TDD 会了是一回事,团队里敢不敢长期这么写是另一回事。它有个常被忽视的软性前提:没有人会因为"先写测试"被当成磨洋工。在压在交付节奏下的团队,先写测试很容易被外行误读成"迟到",于是有人偷偷跳过红灯直接写实现。要让 TDD 站得住,与其讲道理,不如给它一个可观察的保护:把"合并前新增行为必须有配套测试"写进流水线或评审硬门槛,让"没写测试就改功能"在流程上过不去,而非靠个人自觉。
还有一层是节奏上的心理保护:TDD 的反馈是即时的,但一个需求被拆成几十步小循环时,中途的"绿"常常让不熟悉的人觉得"好像没在进展"。这时候要敢于把"这个行为还没落地、红灯正等着被转绿"本身也当成进度,而不只是把最终合拢的东西当进度。把这种心态在团队里摊开讲,比偷偷憋着更利于长期坚持,毕竟多数被放弃的 TDD,不是死于技术,而是死于没有一个愿意共同遵守节奏的环境。
红绿重构之外,TDD 真正的底色是那句慢道理:每一次小的、确定的、有反馈的前进,比一次莽撞的大步更靠得住。红灯亮起说明目标还没到,绿灯亮起说明你此刻是对的,重构让你把这个"对"保持得久。这三步循环之所以值得坚持,不是因为它神,而是因为它把"改代码"从赌场心态变成了一段段可回退、可验证的短行程。