Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

附加章十一:测试

复习

  • 程序:程序由人编写,出错在所难免
  • 模块组合:复杂系统由许多部件组合而成,问题可能藏在结合处

TL;DR

  • 测试用受控方式验证程序行为是否符合预期
  • 输入空间往往无法穷举,所以要选有代表性的用例
  • 单元、集成、系统测试各管一段,层层把关
  • 测试能证明程序有错,却不能证明它没有问题

正文

  程序是人写的,而人会犯错。更麻烦的是,有些错误在平时根本看不出来,直到某个特定输入出现,才突然爆发。于是我们需要一种主动发现问题的办法——测试(testing)。

穷举是不可能的

  最理想的测试,是把所有可能的输入都试一遍。但现实中这几乎永远做不到:一个 32 位整数能取四十多亿个值,组合起来更是天文数字。

  所以测试的关键,不是“试得多”,而是“试得巧”:挑选有代表性的用例,尤其是那些容易出问题的角落——

  • 边界:最大值、最小值、零、空
  • 异常:非法输入、极端情况、并发
  • 典型路径:最常用的正常流程

  经验告诉我们,错误往往不在“常规中间”,而藏在边界和例外里。

一层层把关

  测试通常也不是一次做完,而是分层进行:

  • 单元测试:针对最小的功能单元,检查它自己对不对
  • 集成测试:检查多个单元拼在一起,能否正常协作
  • 系统测试:站在整体角度,验证端到端的行为

  层层叠加的好处是:出错时,问题被定位在较小的范围里,排查更快。这和前面造计算机的思路一脉相承——用一层层抽象把复杂问题拆小。

改完还要再测

  还有一个容易被忽略的环节:回归测试

  当你修好一个错误、或给程序加了新功能,很可能不小心弄坏了原本正常的部分。回归测试就是把过去用过的用例再跑一遍,确保旧的承诺依然成立。它像一把尺子,防止你在前进时把已经走过的路又踩塌了。

测试证明不了“没有错”

  最后要摆正测试的位置。计算机科学家 Dijkstra 有句名言:测试能证明程序有错,却不能证明程序没有错。

  因为测试只能覆盖有限的用例,通过再多,也不能保证下一次输入不会出问题。测试是在降低出错的风险,而不是在证明绝对正确。真正要追求更高的可靠,还需要配合严谨的设计、形式化的推理,以及运行时的监控与防护。

思考题

  为什么说“测试能证明程序有错,却不能证明它没有错”?

小结

知识点

  • 测试用受控方式验证程序行为
  • 无法穷举,应重点覆盖边界与异常
  • 单元、集成、系统测试分层进行
  • 回归测试防止修改引入新问题
  • 测试降低风险,但不证明绝对正确

参考资料

  1. Wikipedia(zh):软件测试:验证软件行为是否符合预期
  2. Wikipedia(zh):单元测试:针对最小功能单元的测试
  3. Wikipedia(zh):回归测试:确认修改未破坏既有功能

思考题答案(仅供参考)

  因为测试只能覆盖有限个用例,而程序可能面临的输入几乎是无穷的。只要有一个没测到的输入会触发错误,程序就仍可能有错。所以测试通过只说明“这些情况下没问题”,无法排尽所有可能。它能发现错误,却不能保证绝对无错。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪