4.4 敏捷下的单元测试
从功能完成后由人手工点一遍走到每次小变更立即得到可定位反馈,沿单元测试隔离、断言、清理和回归边界诊断测试不稳定。
学习目标
- 能沿安排夹具、执行单元、断言结果、清理状态和回归重放追踪一次隔离单元测试
- 能解释敏捷反馈为何依赖快速、确定、可重复的测试,以及时间、网络和共享状态如何破坏确定性
- 能在正常、边界和故障场景中回答:测试随机红绿时,哪一个外部依赖必须被替换或隔离
为什么需要这一机制
本页依据刘欣《码农翻身》(2018 年第 1 版)及出版社公开书目信息,独立重构 4.4 敏捷下的单元测试。正文、代码、图示、实验和练习都是本课程重新设计的教学材料,不复制原书正文、插图、练习答案或代码。
4.4 敏捷下的单元测试 不能停在“多写几个测试”。真实系统要解决的变化是从 功能完成后由人手工点一遍 到 每次小变更立即得到可定位反馈;可执行机制是:单元测试在隔离边界内安排输入、执行行为并断言结果,敏捷迭代依赖快速、确定、可重复的反馈而非测试数量。
三个会让单元测试失真的陷阱
五个目录节点到测试证据
4.4 敏捷下的单元测试
在 ↡在隔离边界内安排输入、执行行为、断言结果并清理状态,为每次小变更提供快速确定反馈的测试机制。 中,最小合同是
但完整的可重放测试还需要清理和回归重放。测试数量不等于反馈质量;确定性、隔离边界和失败定位才决定它能否支持敏捷迭代。
敏捷运动
在 ↡把工作拆成小增量并让自动反馈紧跟每次变化,以缩短发现、定位和修复成本的开发节奏。 中,测试的价值是缩短反馈回路。快不只是执行时间短,还要能在相同输入下给出相同判定,并把失败指向一个小范围变更。
困惑
在 ↡当测试绿色、数量或覆盖率看似良好,却无法解释随机失败与真实行为时出现的诊断状态。 中,先检查测试是否把网络、时钟、共享数据库或环境状态当成隐藏输入。覆盖率高仍可能缺少隔离和可诊断断言。
讨论
在 ↡围绕测试边界、替身、断言强度和清理责任比较不同反馈成本与可信度的分析阶段。 中,把单元测试、集成测试和端到端测试按风险分层。不是所有外部依赖都要伪造,但每种测试都应有清晰速度、范围和失败归因。
一年以后
在 ↡经过持续迭代后回归套件仍能快速、稳定重放历史行为,并在接口变化时暴露真实契约差异的长期状态。 中,测试要能抵抗时间推移:固定时钟、版本化夹具、清理共享状态,定期删除只测实现细节的脆弱断言。
最小可重放实现
fixture = arrange(clock=fixed, network=fake, state=fresh)
actual = act(fixture, input)
assert actual == expected
cleanup(fixture)
assert replay_same_case() == expected这段实现草图只表达单元测试合同,不复制原书叙事或代码。实际运行时应保存夹具、替身、输入、断言、调用记录、清理结果和回归轨迹,使另一位读者能够从干净状态重放。
五步复核一次单元测试
1. 安排隔离夹具
固定时钟、输入、替身、依赖边界和初始状态。先预测正常案例的结果和交互,再运行;不可控依赖要被显式记录,而不是藏在全局环境里。
Lab
单元测试确定性与清理实验
先预测测试边界会影响哪条证据,再切换正常、边界和隐藏依赖样本并重置。
固定时钟和替身,夹具独立,结果与交互稳定
arrange=fresh → act=unit → assert=result+calls → cleanup=zero → replay=match
判定
accept:快速、确定、可重放
当前样本:确定反馈;保存夹具、时钟、替身、输入、断言、调用记录、环境和清理轨迹。
正常、边界与故障证据矩阵
| 样本 | 只改变的变量 | 预期判定 | 必存证据 |
|---|---|---|---|
| 正常 | 夹具、输入、替身和状态均固定 | 结果与交互稳定,清理完成 | 夹具、断言、调用记录、耗时 |
| 边界 | 输入、时间、错误响应或状态改变 | 断言明确,失败可定位 | 样本、预期、实际、首差异 |
| 故障 | 真实时间、网络或共享数据库进入 | 标记不确定性并隔离或升级测试层 | 外部依赖、随机种子、环境、复位 |
故障诊断:先分开隔离、断言与清理
- 夹具侧:查时钟、输入、替身、状态和依赖边界;相同代码随机红绿通常有隐藏输入。
- 执行侧:查被测单元是否越界访问网络、文件、时间或共享数据库;把集成责任误塞进单元测试会放大噪声。
- 断言侧:查返回、异常、调用、顺序和副作用;“没有抛异常”不是完整结果。
- 清理侧:查全局、数据库、文件、mock 记录和线程是否归零;顺序敏感常说明状态泄露。
如果测试随机失败,先固定时钟和随机种子,再隔离网络与共享状态;如果测试全绿但用户行为错误,补强断言和边界;如果运行越来越慢,按风险把外部依赖移到集成层。每次只改变一个依赖,并重放可信基线。
术语表
名词解释
本章出现的专业名词,用大白话再讲一遍。
- 4.4 敏捷下的单元测试
在隔离边界内安排、执行、断言和清理,为小变更提供快速确定反馈的机制。
- 敏捷运动
以小增量和紧邻自动反馈缩短发现、定位和修复成本的开发节奏。
- 困惑
测试看似绿色或覆盖充分,却无法解释随机失败与真实行为的诊断状态。
- 讨论
按边界、替身、断言和成本比较单元、集成与端到端测试的分析阶段。
- 一年以后
回归套件仍能稳定重放历史行为,并在接口变化时暴露真实契约差异的长期状态。
练习
练习
问题 1(4.4 敏捷下的单元测试、敏捷运动): 为什么敏捷团队需要快速、确定的单元反馈,而不是只在功能完成后手工点一遍?
问题 2(困惑、讨论): 测试依赖真实网络时随机红绿,应该直接重试,还是先改变测试边界?
问题 3(一年以后): 如何判断一年后的回归测试仍然保护真实合同,而不是只保护实现细节?
本页小结
4.4 敏捷下的单元测试 的关键不是测试数量,而是隔离、确定、强断言、清理和可重放反馈。完成标准是把五个目录概念落到同一条测试证据链,定位真实时间、网络或共享状态造成的首个偏离,并证明复位后的回归仍可稳定运行。