本节摘要:"先写测试"常被当成质量手段,它更深的作用是设计手段:测试是代码的第一个使用者,使用者的较真会逼出松耦合的接口与可替换的内部结构。本节拆解这条因果链,给出"测试疼点即设计卡点"的诊断法,并演示一次从红灯到新结构的三次挤压。读完你应能主动利用测试先行为设计施加压力,而不是被动等设计变好。
别以为设计是天才画完蓝图、程序员照图施工的产物——在需求按月变的项目里,一次画完的设计三个月后必然走样,问题从来不是"设计得不够远见",而是"设计缺乏随需求进化的机制"。TDD 给的就是机制:每条新红灯都是一次小型设计评审,逼你在最小范围内重新审视结构。设计不是被想出来的,是被循环一格一格挤出来的。
接口好不好用,写实现的人说了不算——他太了解内部了,怎么用都顺。测试不一样:它是第一个只看外部、不看内部的使用者。这个身份带来三重较真:
Checkout 必须连带搭起数据库和真网关,测试就会逼你承认:依赖该注入,而不是内建。第 3 章的替身功夫在这里兑现:能替,说明依赖是显式的、可替换的。rate("aB1") 返回什么时你得想清楚"拒绝"和"原因"要不要分开交付——这一想,Rating 数据类就长出来了。set_global_promo——测试写不下去,正是设计在报警。三重较真合起来,就是"可测试性"的实质:可测试的接口几乎总是松耦合的接口,因为测试无法进入内部较真,只能从边界提要求。
反过来用更妙:测试的疼点是设计卡点的探测器。积累了一个版本周期的实战对照表如下:
| 测试的疼点 | 背后的设计卡点 | 重构方向 |
|---|---|---|
| 构造被测对象要连带搭一堆陪葬品 | 构造函数职责过重 | 依赖改注入,陪葬品换成替身 |
| 断言要深入对象内部翻字段 | 职责泄漏,状态外翻 | 补行为方法,让内部保持私有 |
| 一条业务规则散在三个测试里 | 规则没有归属,逻辑游离 | 收拢到一个领域对象 |
| mock 链一路打点穿三层 | 违反得墨忒耳定律,穿透陌生人 | 在中间层补一个讲人话的方法 |
| 每加功能要改十条测试 | 测试锁了实现细节 | 断言从结构词改行为词 |
这张表值得贴在工位上——它把"测试代码坏味道"直译成"设计坏味道",用测试当探针,比定期重构评审便宜得多也及时得多。
看灯塔小组的实际案例。新需求:"黑名单用户的订单不能使用任何优惠券。"第一条红灯:
def test_blacklisted_user_gets_no_coupon_discount(): pricing = Pricing(blacklist=InMemoryBlacklist(["u-666"])) order = an_order().by("u-666").with_coupon("SAVE20").build() assert pricing.payable(order) == order.raw_total() # 券不计入
写这条测试时较真立刻发生:Pricing 现在需要"查黑名单"的能力——测试逼你决定黑名单从哪来。注入(如上),还是让 Pricing 自己去查库?前者测试好写,后者测试要陪葬数据库。选择注入,第一次挤压完成:黑名单成为显式依赖。
第二次挤压来自断言:payable 这个名字是写断言时才确定的——原来函数一直叫 total,语义含混"折前折后";要断言"黑名单不享受券",接口必须能表达"应付"这个概念。红灯逼出了更准的词汇。
第三次挤压在重构窗口:黑名单判断与券计算搅在同一个方法里,下一轮"白名单加倍"需求必然冲突。提取 _apply_coupons 时把黑名单检查挪到券逻辑上游,策略结构(第 4.2 节的主角)顺势接管。三次挤压,没有任何一次坐在白板前"做设计",结构却在每个红灯的推动下挪到了它该在的位置。三次挤压的方向也值得注意:没有一次是"加抽象",全是"挪位置"——浮现式设计的动作通常是移动和命名,而不是发明新概念,这也是它比想象中便宜的原因。

⚠️ 别把"测试驱动设计"理解成"测试决定设计"。测试提供的是压力与时机,取舍仍要人做——黑名单注入还是内查,测试只负责让两个选项的成本显形,拍板的永远是你。
机制讲完,值得换个完全不同的需求复验因果链是否成立。新需求:"日报要在每天早上八点推送到值班群。"红灯逼出的东西依次是:推送目标得是可注入的通道协议(不然测试要真发消息)——较真一;should_send_now 之类的判断得能被假时钟驱动(5.3 的伏笔在此接上)——较真二;"日报"与"推送"被分成两个对象,因为断言要求能单独验证内容生成而不碰通道——较真三。三个不同的需求,浮出来的却是同一批结构类型:注入的协作方、讲业务话的接口、单一职责的边界。这不是巧合,是"第一个使用者"较真角度的必然产物,也是你可以在自己项目里反复验证的预言。
💡 自查一条:回顾你最近一个"测试很难写"的模块,按上面对照表找疼点对应的卡点。十有八九,你会在改完设计的当天发现测试变好写了——这个正反馈就是本章全部内容的体感版。