第 13 / 22 篇

第 12 章:用故障矩阵验证合同

一个正常演示只能证明某条成功路径走通。网关真正的工程难点往往发生在两个动作之间:上游已经开始而客户端尚未收到内容,usage 已到达而 done 尚未到达,取消到达而结算也正在完成。测试需要把这些窗口变成可控输入。

12.1 把测试对象拆成三个层次

协议层测试解析规则,例如 UTF-8 分片、多行 data 和未完成事件。策略层测试资格与预算,例如能力不匹配时不联系上游。集成层通过真实 HTTP 连接观察响应和上游调用次数,验证异常路径没有被函数替身隐藏。

三层互相补充。只有策略函数测试,无法发现写入超时后的资源清理问题;只有端到端成功演示,又很难定位一个多行事件为何解析错误。随书实验用本地服务替身控制故障,仍然经过 HTTP、SSE 和客户端。

12.2 矩阵中的每一行要有证据

注入情况 应保持的规则 至少检查的证据
请求能力无候选支持 不启动不合格上游 上游接收次数为零
默认请求收到未开始拒绝 不自行获得切换授权 后备上游接收次数为零
显式授权且可信未开始拒绝 尝试受限、结果可解释 两个尝试记录与完整结果
普通 503 或发送后超时 不把不确定变成安全重放 后备次数为零、未知用量
输出部分文字后断开 不拼接新的生成 客户端部分内容、终止状态与账本
usage 缺失或不符合合同 不确认结算 预留仍在,且有异常分类
另一租户请求取消 不影响目标运行 拒绝结果、原运行继续
慢消费者 缓冲受限且有停止路径 队列观测、写入超时、线程退出

检查一个日志里的 success 字符串,不足以证明这些规则。上游次数应来自替身的独立计数;账本应读取实际保存的状态;输出应由客户端解析。这样能减少“实现和测试重复同一个错误”的风险。

12.3 用同步条件定位窗口

并发测试若大量依赖固定 sleep,会在不同机器上变得脆弱。更好的方法是等待明确条件:替身已经接受请求、首个事件已被客户端读取、账本记录已建立,然后注入下一步动作。

等待本身也应有上限,并在失败时输出目前状态。否则一个测试会因后台线程阻塞而永久挂住,CI 看起来像网络故障,却没有定位信息。

慢消费者测试尤其要谨慎。操作系统缓冲可能先容纳一段输出,让测试误以为已经证明应用背压。实验需要足够的数据量、受限的应用队列、受控读速率和可观察停止条件,同时承认缓冲区大小与调度会影响时间。

12.4 回归报告描述行为而非排名

本书测试没有比较真实模型质量,也没有进行供应商吞吐排名。它验证确定性宿主行为和特定故障窗口。真实模型评测需要额外的数据集、任务验收与重复试验,不能把替身测试通过率写成模型可靠性。

保存运行记录时,至少带上代码版本、Python 版本、命令、测试名称、结果和失败输出。变更后重新运行受影响的测试;发布前运行整套,并实际打开生成的在线页与 EPUB。静态文档与测试各有范围,谁都不能替代另一个。

练习。 把“已经输出以后不重试”改写成一个测试规范。至少列出上游计划、客户端观察、后备调用次数和账本终态,避免仅断言某个内部函数没有被调用。