跳到主要内容

测试:冲着坏消息去 — 三个悖论和一个算术

这一章讲四件事: 测试分几层、计划为什么必须先行;三个悖论(它们决定了测试的目的); 错误聚集定律(它决定测试的靶子);以及进度不够时那道选择题——书里算给你看, 为什么「把没测完的东西拼一起赌一把」几乎必输。

1. 先看现象:「测试全过了」之后的第三次宕机

测试报告写着:用例通过率 100%。上线第三周,系统还是挂了。

问题出在哪?出在「通过率」这个词给的安全感上。本章先给三个事实, 它们合起来解释了上面的事故,也决定了测试该怎么做:

  1. 测试跑过的输入,永远只是所有可能输入的极小一部分(后面给机制);
  2. 「用例都过了」度量的是用例,不是软件——用例没覆盖到的地方,过了等于没测;
  3. 漏测的缺陷不均匀分布——它们扎堆

先立三个词。单元测试:对单个独立组件的测试;集成测试:对组件拼起来的组合的测试, 重点看接口(两块软件之间约定好的联系方式);系统测试:对整个系统、对照需求规格说明的测试1

2. 顶层全景

测试的目的(三个悖论改写)
悖论1 测试只能证明缺陷存在,不能证明不存在
悖论2 成功的测试 = 发现了错误的测试
悖论3 零错误对价值什么也没说


既然目的是「找」,靶子在哪?
错误聚集:一半错误在 15% 的模块;80% 在 2%(更狠的口径)


怎么找:黑盒(按外部行为出题)+ 白盒(按代码内部路径出题)
非法输入 · 压力测到 x+1 · 用例必须写明期望结果


什么时候算完:数「每周新发现错误率」或「埋的 bug 找回多少」,
不数「用例通过率」(那是无效指标)


进度不够时的选择题:承认落后 vs 大爆炸集成(主走查看算术)

图说:每一层都被上一层逼出来:目的变了,靶子、打法、完成标准全跟着变。

3. 核心原理

3.1 计划先行,并且每一层都能追回需求

计划先行(原则 108):别等软件写完才挠头「这玩意怎么测」。 顺序是倒着的:需求定稿,测试计划员就要从「可测试性」角度审需求;集成测试计划在初步设计定稿; 单元测试计划在详细设计一完成就动工2

追回需求(原则 107):再建一张第 05 章 5.2 那样的二维表,这次行=测试用例,列=需求: 交叉格画 1 表示「此用例验证此需求」3。查法同款:

  • 横查: 一整行没画 1 → 这条测试没有目的,纯属仪式;
  • 竖查: 一整列没画 1 → 那条需求没被任何测试覆盖(漏测)。

排序还免费送:需求的优先级(第 04 章 3.4 的 M/D/O)取最大值,就是对应测试的优先级3

3.2 主走查:进度不够时,那道选择题的算术

原则 119 的场景每个项目都遇过,书里把账全算好了,整段走完4:

设定: 按计划,单元测试、集成测试、系统测试各 2 个月,共 6 个月。 现在距交付日只剩 1 个月,而单元测试只完成了 50%

第一步:算落后多少。 单元测试还差 1 个月,集成测试 2 个月、系统测试 2 个月没动—— 按正常顺序,你已经落后 5 个月

第二步:摆出两个选项。

  • 选项一:向客户承认 5 个月延误,请求推迟;
  • 选项二:把所有组件直接拼成整体(包括那 50% 没做单元测试的),期盼奇迹——书里给这个机会的 概率(可能性大小,用百分数表示)量级是 0.001%

第三步:看选项二实际发生什么。 拼起来的系统必然不跑,然后你要在接口错误没测过的组件错误之间猜是哪个——书里原则 123 点破:大量时间会耗在「确定哪一个是真正的原因」上5。 作者的判断:选二的后果通常是排期再延 6 个月——比选项一还多赔 1 个月4

为什么经理总选二? 书里说得刻薄而准确:因为选二「看起来像在承认失败之前竭尽全力」4。 这是给管理者的旗语,也是给工程师的:不能靠跳过单元测试和集成测试来节省时间。

3.3 悖论一:测试只能证明「有」,不能证明「没有」

Dijkstra 那句被书里郑重引用的话:无论测得多彻底,测试只能揭示缺陷存在,不能确保缺陷不存在; 想「证明正确」,要的是完全不同的另一种方法(正确性证明)6

机制在哪?原则 115 顺带给了:测试只能动用输入域里极小一部分可能的值7。 一个收两个整数的程序,可能的输入已经是天文数字;收一段文本的程序,可能输入比宇宙原子还多。 跑一百万个用例,等于在无边的海里舀了一瓢——瓢里没鱼,不证明海里没鱼。

由此直接推出完成标准不能是「测够了就证明没问题」(3.7)。

3.4 悖论二:「测试成功」的意思是「找到了错误」

原则 113 用了一个完整的类比,值得整段搬进脑子8:

你病了,医生把你的血样送去化验。几天后医生来电:「好消息!你的血液正常。」 ——这不是好消息。你病着,否则不会去化验。一次成功的化验,是查出了你哪里有问题的那次。

软件同理:程序里有缺陷(否则不用测),所以成功的测试=发现了缺陷的测试。 「好消息!测试跑通了」是错误的态度。这个立场立刻推出两条纪律:

  • 别测自己写的软件(原则 109):测试期间的正确心态是「盼着暴露缺陷」, 而开发者对自己孩子的心理恰恰相反——带着「不想发现问题」的偏见,测试必然变松9; 独立测试员至少接管:集成测试、系统测试、以及「单元测试做得够不够」的复核;
  • 连测试计划也别让自己的开发者写(原则 110):写软件时做错的那个假设 (比如「输入不会超过 100」),写测试时会原样再犯一次——出题人不能是答题人10

书里还留了一个诚实的括号:Cleanroom 学派有相反主张,且今天也有让开发者自测的思潮 (中文版译者注也点了这一点)——但「盼坏消息」的心理机制没变9

3.5 悖论三:零错误不等于有价值

温伯格的「无差错谬论」,第 02 章已经引过,这里给它在测试语境的完整形态11: 大量错误当然证明软件没用;但零错误对价值什么也没说—— 如果你开发的根本不是客户要的系统,那么形式化方法、全部测试、全部产品保证加起来都是浪费。 这条悖论把测试放回了它的位置:测试管「把东西做对」,不管「做的是对的东西」——后者是第 03/04 章的管辖。

3.6 靶子:错误扎堆,而且越扎越紧

原则 114 给了三档数字,一档比一档惊人12:

大型系统中,约一半错误出现在 15% 的模块里;80% 的错误出现在 50% 的模块里。 Okimoto 与温伯格的口径更狠:80% 的错误集中在 2% 的模块里。

推论直接可执行:测试日志(逐条记录「何时、在哪个模块、发现了什么错误」的流水) 不仅记「这个月发现多少错误」,还要记「每个模块各发现多少」; 某个模块历史上持续多产错误,别再修了——从头重写,按第 06 章的简单标准写,别按「聪明」写12。 这条到第 12 章还会再硬一次:发布前错误多的模块,发布后错误也多。

3.7 打法四件套与「什么时候算完」

黑盒+白盒(原则 115):黑盒测试只看外部行为出题(输入该得什么输出); 白盒测试扒开代码、按内部路径出题(比如「50 条指令以内的程序所有路径都要走到」)。 书里的例子精确展示为什么两个都要13:

需求说「打印输入列表中所有数字的总和」。程序员埋了一个后门:一旦输入里出现 213,总和清零。 黑盒测不到它——需求里没有 213,除非碰巧随机抽中含 213 的用例; 白盒要求路径覆盖,那个「等于 213」的分支必须被走到,几乎必然现形。

非法输入(原则 117):给 0~100 范围的整数列表排序,别只测正常列表—— 负数、全部相等的数、非整数、字符、空记录,全都该有专门用例14

压力(原则 118):需求写「每小时最多 x 个」,就测到 x+1、x+2—— 系统管不住环境,别在环境失灵的那天崩盘(和第 04 章 3.6 的「101 架飞机」同一件事)15

期望结果(原则 116):每个用例必须写明期望输出。不写,测试员无法判定成败, 还会潜意识把错的判成对的(盼着它对);更糟的是把对的判成错的,一队人去「修」正确的代码16

什么时候算完(原则 121):「时间到了」是政治答案不是质量答案。有效的完成度指标 (用来衡量某件事的数)有两个:每周新发现错误率(该降下来);bebugging—— 故意往代码里埋 20 个已知缺陷(数是演示编的),测试找到 15 个 → 估计真实缺陷也被找到了约 75%, 还剩约四分之一没被这套测试触到,而且埋的那 5 个正好暴露测试的盲区17。 书里点名了一个无效指标:「用例通过率」——除非能证明用例覆盖了需求,通过率说明不了软件质量17。 (补一嘴机制:覆盖率度量——语句/分支/路径覆盖率,即被跑到的代码行、岔路、全程路径的百分比—— 是工具可以自动统计的,但它们同样只证明「走到了」,不证明「走对了」18。)

配套三小件:在软件里埋测量探针(记录执行轨迹、异常、调用)以便失败后取证19; 每个错误分析原因——技术原因之外还有管理原因(「本该在集成测试前手动核对一次」),并告知全员20; 对错不对人:写软件要求的细致程度没人能达到,错误是学习材料,不是罪证21。 书里另有一条文化层的:日本业界把产品缺陷视为公司之耻、工程师之耻,被指出错误应心存感激并广而告之22

4. 作者的判断与证据

说法谁的证据
测试只证存在不证不存在作者引 Dijkstra6论证(输入域不可穷尽);无实验
一半错误在 15% 模块/80% 在 2%作者引 Endres、Okimoto/温伯格12多份实测统计,口径间有差异
审查比测试更能发现错误第 07 章引 Fagan 的 82%实测;与本章互为表里
大集成再拖 6 个月作者的经验判断4案例推算;「0.001%」是修辞量级
McCabe 指标好用(1995)→「不再有用,对不起汤姆」(2021)作者本人两版自述232021 版亲手撤销;「我知道没有人再使用它了」

判断(我们的,不是书里的): 把 3.6 的聚集定律和 3.1 的追溯表合起来,现代测试策略可以压成一句: 按历史错误分布分配测试预算,按需求优先级分配覆盖义务——前者告诉你火力往哪集中(那 2% 的模块), 后者告诉你底线在哪(每条高优先级需求至少一个用例)。只用其中一张表,都会把资源撒平。 如果错,会错在: 如果这是一个全新系统,没有「历史错误分布」可言,聚集定律暂时无处落脚—— 此时唯一可用的是需求优先级那张表,聚集表要等第一轮数据回来才有资格说话。

5. 边界与局限

  • 「别测自己的软件」在 2020 年代被广泛松动(作者书里就引了 Cleanroom 的反例9; 中文版译者注也补充:现在的倾向是程序员自测)。松动成立的前提是开发者真能切换到「盼坏消息」的心态; 判据看结果:自己的模块是不是总在集成阶段才暴雷。
  • bebugging 今天很少真做17——埋缺陷的成本和心理阻力都大;现代等价物是「突变测试」(工具自动制造变体), 书里没有提,算我们补的背景。
  • McCabe 指标已被作者撤回23:书里的圆数公式(把程序画成图,数节点和边)作为历史看看即可; 它想解决的问题(估计测试难度)今天由覆盖率工具接管。
  • 大爆炸的算术默认各测试阶段不可压缩、不可并行。现实中边界情况可并行一小部分, 但「跳过单元测试直接集成」没有并行红利,只有重复排障的利息——结论不变。

6. 可带走的

  1. 测试的目的是找坏消息:成功的测试=发现了错误的测试;「跑通了」本身不是好消息;
  2. 测试永远不能证明无缺陷——瓢里没鱼不证明海里没鱼;别拿「测过了」当安全声明;
  3. 零缺陷对价值什么也没说:做错了系统,零错误只是精准地白干;
  4. 错误扎堆:一半错误在 15% 的模块;按模块记错误账,常错的模块重写而不是再修;
  5. 黑盒+白盒缺一不可:埋进代码的「213 后门」,只有白盒的路径要求能抓住;
  6. 用例必须写明期望结果;非法输入、压力上限 x+1 都要有专门用例;
  7. 测试让独立的人做、测试计划让独立的人写——出题人不能是答题人;
  8. 完成度数「每周新错误发现率」或埋雷找回率,不数「用例通过率」;
  9. 进度不够时,承认 5 个月延误,好过赌 0.001% 的大集成再赔 6 个月。

7. 原文地图

主题原书章原文位置
三层测试定义第6章 测试原则text/18-ch06.txt:9(搜「单元测试」) · text/18-ch06.txt:11(搜「集成测试」)
测试追溯需求(横查/竖查)第6章 测试原则text/18-ch06.txt:29(搜「漏测」)
测试计划先行(可测试性)第6章 测试原则text/18-ch06.txt:39(搜「可测试性」)
别测自己的软件第6章 测试原则text/18-ch06.txt:63(搜「自己的软件」)
别写自己的测试计划第6章 测试原则text/18-ch06.txt:67(搜「相同的错误」)
只证存在不证不存在第6章 测试原则text/18-ch06.txt:81(搜「正确性」)
无差错谬论第6章 测试原则text/18-ch06.txt:89(搜「无差错谬论」)
成功的测试发现错误(验血)第6章 测试原则text/18-ch06.txt:103(搜「血样」)
错误聚集(15%/2%)第6章 测试原则text/18-ch06.txt:113(搜「15%」) · text/18-ch06.txt:113(搜「仅仅2%」)
黑盒白盒(213 后门)第6章 测试原则text/18-ch06.txt:125(搜「213」)
用例含期望结果第6章 测试原则text/18-ch06.txt:133(搜「期望的正确结果」)
非法输入第6章 测试原则text/18-ch06.txt:145(搜「负数」)
压力测试 x+1第6章 测试原则text/18-ch06.txt:155(搜「x+1」)
大爆炸算术(5 个月/0.001%)第6章 测试原则text/18-ch06.txt:163(搜「5个月」) · text/18-ch06.txt:169(搜「0.001%」)
McCabe 指标与曲奇刀第6章 测试原则text/18-ch06.txt:177(搜「曲奇」)
完成度指标与 bebugging第6章 测试原则text/18-ch06.txt:199(搜「bebugging」)
覆盖率三种第6章 测试原则text/18-ch06.txt:211(搜「语句覆盖率」)
单元测试前别集成(脚手架)第6章 测试原则text/18-ch06.txt:227(搜「脚手架」)
埋测量探针第6章 测试原则text/18-ch06.txt:235(搜「执行轨迹」)
分析错误原因(含管理原因)第6章 测试原则text/18-ch06.txt:245(搜「管理问题」)
对错不对人第6章 测试原则text/18-ch06.txt:253(搜「学习经历」)
被指出错误应感激(日式)第2章 一般原则text/14-ch02.txt:287(搜「心存感激」)
2021:McCabe 撤回作者序text/08-fm.txt:57(搜「对不起」)
2021:测量已自动化作者序text/08-fm.txt:59(搜「自动」)

Footnotes

  1. 出处:「第6章 测试原则」第 9 段(text/18-ch06.txt:9,搜「单元测试」)、第 11 段(text/18-ch06.txt:11,搜「集成测试」)、第 39 段(text/18-ch06.txt:39,搜「系统测试」)。

  2. 出处:「第6章 测试原则」第 39 段(text/18-ch06.txt:39,搜「可测试性」)。「软件开发人员会先创建产品,然后挠头说『现在我们要如何测试』」同段。

  3. 出处:「第6章 测试原则」第 29 段(text/18-ch06.txt:29,搜「漏测」)。空行=无目的测试、优先级=需求优先级最大值,同段。 2

  4. 出处:「第6章 测试原则」第 163 段(text/18-ch06.txt:163,搜「5个月」)。两个选项、0.001%(搜「0.001%」)、「排期再延长 6 个月」「看起来似乎他们在承认失败之前竭尽全力」「不能通过忽略单元测试和集成测试来节省时间」同段。 2 3 4

  5. 出处:「第6章 测试原则」第 227 段(text/18-ch06.txt:227,搜「脚手架」)。接口错误与未测组件错误难分、尽早做集成测试计划、临时脚手架模拟缺失组件,同段。

  6. 出处:「第6章 测试原则」第 81 段(text/18-ch06.txt:81,搜「正确性」)。 2

  7. 出处:「第6章 测试原则」第 123 段(text/18-ch06.txt:123,搜「很小一部分」)。

  8. 出处:「第6章 测试原则」第 103 段(text/18-ch06.txt:103,搜「血样」)。按发现错误的可能性选测试、按「善于发现错误」考核测试组,同段。

  9. 出处:「第6章 测试原则」第 63 段(text/18-ch06.txt:63,搜「自己的软件」)。独立测试员的三个场景、Cleanroom 反对的括号、中文版译者注的补充,均在此段及译者注。 2 3

  10. 出处:「第6章 测试原则」第 67 段(text/18-ch06.txt:67,搜「相同的错误」)。

  11. 出处:「第6章 测试原则」第 89 段(text/18-ch06.txt:89,搜「无差错谬论」)。「如果你在开发错误的系统,那么世界上所有的形式化方法、所有的测试和所有的产品保证都将于事无补」同段。

  12. 出处:「第6章 测试原则」第 113 段(text/18-ch06.txt:113,搜「仅仅2%」)。「保守估算……大约一半的软件错误出现在15%的模块中」同段;「从头开始重写」见第 115 段(text/18-ch06.txt:115,搜「从头开始重写」)。 2 3

  13. 出处:「第6章 测试原则」第 125 段(text/18-ch06.txt:125,搜「213」)。黑盒除偶然外测不到、白盒路径覆盖能抓到,同段。

  14. 出处:「第6章 测试原则」第 145 段(text/18-ch06.txt:145,搜「负数」)。

  15. 出处:「第6章 测试原则」第 155 段(text/18-ch06.txt:155,搜「x+1」)。

  16. 出处:「第6章 测试原则」第 133 段(text/18-ch06.txt:133,搜「期望的正确结果」)。潜意识、把对的判错,同段。

  17. 出处:「第6章 测试原则」第 199 段(text/18-ch06.txt:199,搜「bebugging」)。每周错误率、「测试用例通过的百分比」是无效指标,同段。20/15/75% 为演示编的数,机制比例来自原则本身。 2 3

  18. 出处:「第6章 测试原则」第 211 段(text/18-ch06.txt:211,搜「语句覆盖率」)。「别自欺欺人地认为程序在任何定义下都是正确的」同段。

  19. 出处:「第6章 测试原则」第 235 段(text/18-ch06.txt:235,搜「执行轨迹」)。

  20. 出处:「第6章 测试原则」第 245 段(text/18-ch06.txt:245,搜「管理问题」)。

  21. 出处:「第6章 测试原则」第 253 段(text/18-ch06.txt:253,搜「学习经历」)。

  22. 出处:「第2章 一般原则」第 287 段(text/14-ch02.txt:287,搜「心存感激」)。广而告之的两个好处(帮别人避开、后续修错不抵触)同段。

  23. 出处:「作者序」第 57 段(text/08-fm.txt:57,搜「对不起」);1995 年原评价见「第6章 测试原则」第 177 段(text/18-ch06.txt:177,搜「没有理由」)。 2