该测什么、不该测什么:一张判断表
二十三章走完,你有了完整的工具箱和一套判断力。最后一章不加新东西,只做两件事:把前面所有的取舍压成一个你能在写代码时当场用出来的决策流程;然后,回到第 1 章那段 AI 写的、看起来完美的代码,把这本书想说的话讲完。因为这本书从头到尾其实只在回答一个问题 —— 在一个代码越来越多由不知道对错的机器写出来的世界里,你怎么知道它错没错。答案不是"更信任 AI",也不是"测试万能"。答案要诚实得多,也实在得多。
一张判断表
面对一段代码,该不该测、测到哪一层、用什么手段?下面这个决策表把全书的取舍串成几个问题。选一选,看它给的建议 —— 每条建议都指回对应的章节。
它没有标准答案,只有权衡。但几条主线是清楚的:纯逻辑往单元层砸(还可以加属性测试)、带副作用的往集成层放(用 fake 隔离)、UI 别堆端到端(快照兜住长相)、代价能要命的路径全测到、AI 生成的和遗留代码要额外用变异测试确认测试有牙齿。
什么不值得测
这本书讲了二十多章"怎么测",最后必须诚实地说一句:不是所有东西都值得测。过度测试和测试不足一样是浪费 —— 它耗成本、拖速度、还得跟着代码一起维护。
- 无逻辑的样板:纯粹的 getter/setter、只是转发的一行函数、数据类本身。它们没有会出错的逻辑,测了只是把代码抄一遍。
- 框架/库的行为:别测 Room 会不会存、别测 Kotlin 的
+对不对。那是框架作者的测试职责,不是你的。你该测的是你怎么用它(第 6 章的接缝)。 - 一次性脚本:跑完就扔的代码,代价又无所谓 —— 测试的维护成本大于收益。
- 还在剧烈探索、随时会推翻的原型:方向都没定,把行为钉死反而是负担。等它稳定了再补。
判断标准始终是那把尺子(第 1、23 章):这个测试"本可能有效失败并因此救你一次"的概率,值不值它的写与维护成本?值,就测;不值,别为了数字而测。
一份可以贴进 PR 模板的自检清单
把全书浓缩成一张 review 时能逐条对照的清单 —— 既用来审自己的测试,更用来审 AI 写的测试:
验证到位吗
- ☐ 每个测试都有真正的断言,不是只"跑一遍没崩"(第 15 章)
- ☐ 期望值是独立算出来的,不是从代码输出里抄的(第 2、18、19 章)
- ☐ 测了边界值和失败路径,不只是 happy path(第 9 章)
健壮吗
- ☐ 测的是行为(结果/状态),不是实现(调用序列)——不改行为、重写实现,它不该红(第 11 章)
- ☐ 没有
Thread.sleep、没有真时钟/随机、不依赖执行顺序(第 12、17 章) - ☐ mock 只在架构边界,内部尽量用真/fake(第 10、11 章)
好懂吗、跑得动吗
- ☐ 名字说清了"给定-当-则",失败时一眼知道坏在哪(第 2 章)
- ☐ 一个测试一个失败理由;Arrange 只突出相关字段(第 2、23 章)
- ☐ 放在了对的层(纯逻辑别上真机、别堆 e2e),整套够快(第 4、20 章)
如果是 AI 写的
- ☐ 它有没有把当前(可能有 bug 的)行为当成期望值固化下来(第 19 章)
- ☐ 关键逻辑跑一遍变异测试,确认这些测试真有牙齿(第 16 章)
测试的性价比:一条能心算的公式
「该不该测」归根结底是笔账。不用精算,但心里有个粗略的天平会帮你做决定:
- 出错概率高 → 值得测:复杂逻辑、多分支、并发、边界密集、你(或 AI)没把握的地方。
- 出错代价高 → 值得测:碰钱、碰数据、碰安全、碰不可逆操作。哪怕概率不高,一次就要命。
- 写与维护成本高 → 压低期望:难以隔离、依赖一堆真环境、变动极频繁的代码。这类要么先改造使其可测(第 5 章),要么诚实地少测。
这条心算解释了本书所有的取舍:为什么纯逻辑该多测(概率高、成本低)、为什么样板 getter 不用测(概率≈0)、为什么支付路径要测到牙齿(代价爆表)、为什么一次性脚本不值得测(代价≈0、成本>收益)。不是「测得越多越好」,是「把测试花在天平最重的那一侧」。
拿第一章那段代码走一遍
把决策表用在开篇那个 average 上,看它怎么一路指到具体做法:
- 单元测试为主:纯逻辑的甜点区。穷尽边界 —— 空列表、单元素、负数、全零(第 5、9 章)。
- 加一条属性:「结果永远是列表里某个值之间的一个有限数」,让引擎替你撞边界(第 13 章)。
- 因为是 AI 写的:先别信它自带的测试。你独立算期望值,补它漏的空列表,再对它跑一次变异测试确认有牙齿(第 16、19 章)。
三步走完,那个「average([]) = NaN」的 bug,在它合进主干之前就被摁住了。这不是运气,是流程 —— 而这套流程,就是这本书。
回到第一章:测试到底兜的是什么底
现在可以把这本书的话讲完了。
回到第 1 章那段 average。AI 给了你它确信正确的代码,而它的确信里没有空列表那一行反例。整本书,都是在处理这一个处境 —— 只不过把它拆成了单元、集成、边界、属性、变异、CI……二十多个侧面。但内核一直是同一个:
AI 让写代码变得几乎免费。于是瓶颈从"写"移到了"判断对错" —— 而判断对错,恰恰是 AI 做不到的:它没有一个来自外部的、能反驳自己的声音,它只拿自己的模式评判自己的输出。
测试,就是那个外部的声音。它把代码真的跑起来,拿事实去撞你写下的期望。它不在乎 AI 多自信、代码多漂亮 —— 它只报告:这个输入下,它到底对不对。这是整条链路里,唯一一个不由"谁觉得对"、而由"事实"说了算的环节。
而这本书反复强调的那条线是:正因为它如此关键,"什么是对的"这个定义,绝不能也塌回 AI 自己的闭环里。你可以让它写代码、搭测试的架子、去找反例 —— 但"对"的定义和"它到底验证了没有"的判断,得握在你手里。这不是不信任 AI,这就是"验证"这个词的定义:验证之所以是验证,就因为它独立于被验证的对象。
所以,测试兜的是什么底?
它不能证明你的代码是对的 —— Dijkstra 和 Popper 早说过,那是原理上做不到的事。它也不是唯一有用的东西 —— 类型系统、review、监控、灰度,都在同一条防线上。
但它能做一件别的东西都替代不了的事:当 AI 那份完美的自信里藏着一行反例,当那段"看起来毫无问题"的代码即将在某个边界上崩掉 —— 一套好测试,让你成为第一个知道的人。不是在凌晨三点的线上事故里,不是在用户的差评里,而是在它合进主干之前,在 CI 那盏灯由绿变红的那一瞬间。
这就够了。在一个越来越多东西由不知道对错的机器写出来的世界里,「第一个知道它错了」——这件事的价值,只会越来越大。
1. 把上面那份自检清单,真的贴进你团队的 PR 模板。下一个 PR 就开始用它审 —— 尤其是审 AI 写的那部分。
2. 回到第 1 章那个 Demo,再点一次"让测试去证伪"。这次你已经知道,那不是一个关于 bug 的把戏,是这整本书的第一性原理。
3. 下次 AI 递给你一段"没问题"的代码,别问它对不对。想办法让它露馅 —— 然后你就懂了「证伪」。
测试不能证明代码是对的。
它只是让你,第一个知道它错了。