← 返回

工程正确性(一):测试全绿仍可能违反规格

系列导航:11 | 12 | 13 | 14

有一次工具链模块的 Maven 测试达到 110 个通过,构建也显示成功。随后按规格逐项检查,仍发现三个问题:启动 Java 进程时参数被拼成一个字符串,删除镜像请求带了过大的 bearer scope,注册表探测把 401 当成服务可用。测试覆盖了代码路径,却没有覆盖协议的精确定义。

这类问题很难靠继续堆单元测试解决。测试断言、实现和假服务都来自同一份理解时,三者会稳定地一起通过。正确性需要把规格、实现、独立参考和运行行为放在同一张检查表中。

flowchart TD Spec[规格与协议] --> Contract[契约清单] Contract --> Unit[单元测试] Contract --> Integration[真实服务契约测试] Contract --> Review[人工边界审查] Unit --> Evidence[差异与证据包] Integration --> Evidence Review --> Evidence Evidence --> Gate[发布门禁]

绿灯的含义要写清

单元测试通过表示输入和断言范围内的函数行为符合预期。它不自动证明参数序列化符合命令行语义,不证明权限范围足够窄,也不证明第三方服务对状态码的解释正确。构建成功只说明编译、打包和被启用的测试任务完成,项目默认跳过测试时尤其要显式检查 -DskipTests=false

每个关键规则写成可观察的证据:命令行参数保存为参数数组,删除操作的 scope 断言为 pull,push 以外的权限被拒绝,注册表探测把 401 分类为“服务响应但需要认证”,而非健康。断言越接近协议字段,错误越容易被定位。

从规格拆检查项

先把规格拆成输入、输出、状态码、权限、超时和副作用。实现审查逐项标记证据来源:标准测试向量、第三方服务响应、独立实现、日志或运行截图。没有证据的项保持未验证,不用“已有测试”代替。

接口测试使用真实 HTTP 或接近真实的契约服务器,fake 只模拟网络异常和固定响应。fake 必须拒绝真实服务会拒绝的输入,接受范围过宽会掩盖参数错误。对 CLI、JNI、浏览器和数据库边界,至少准备一组字节或命令行级别的断言,不能只测高层对象。

性质和变形测试

算法或数据处理难以枚举期望值时,加入性质测试:排序保留元素多重集合,金额拆分总和守恒,状态转换不跳过必需阶段,签名篡改后验签失败。变形测试对输入做允许的变换,再检查输出关系,能覆盖示例测试没有触及的组合。

随机测试保存固定反例和生成器版本。只保存随机种子会因生成器改变而无法重现。失败样本缩减后进入回归集,回归集再由人工确认业务含义。

代码通过后的协议复核

复核要跳出实现本身,逐项对照规格检查四个问题:

  1. 参数是否按对方解析方式传递,尤其是字符串、数组和空值。
  2. 权限是否只申请当前操作必需的范围,错误码是否被正确分类。
  3. 成功响应是否真的产生了预期副作用,失败是否留下部分状态。
  4. 文档、测试、运行配置和打包产物是否描述同一行为。

这一步发现的差异不应直接归类为“测试不稳定”。如果规格修正了预期,保留差异报告、旧行为、变更原因和迁移影响。零差异不是目标,未解释的差异才是发布阻断项。

线上监控补上测试盲区

算法不一定抛异常,线上需要观察业务不变量:任务状态只按允许路径变化,删除请求不会扩大权限,发布包 checksum 不变,成功任务确实产生可查询结果。告警带上版本、请求 ID 和错误分类,不记录密钥或用户内容。

这套方法不能保证没有遗漏。它把“全绿”降回一个明确的证据点,并迫使团队把规格、运行环境和第三方语义纳入验收。

参考资料