AI 编码的测试证据门禁:为什么通过用例还不够

AI 编码的测试证据门禁:为什么通过用例还不够
Spec Coding 编辑部 · Spec-First 工程实践内容

AI 写出的代码读起来都像能跑。有时候它甚至能通过自己写的那套测试。我现在的规矩是:不信代码,不信测试,只信"这些测试真的能抓住真实 bug"的证据。下面讲的,就是我在合并前如何把这份证据落成具体文件。

发布于 2026-03-10 · ✓ 已更新 2026-05-06 · 阅读约 7 分钟 · 作者:Spec Coding 编辑部 · 审校:编辑与事实核查政策

最吓人的是能通过评审的幻觉

我批过最吓人的一个 AI 生成 PR,是支付拒付处理器。测试全绿,diff 读起来干净利落,助手还顺手加了一个完整的测试文件贴在实现旁边。两周后我们发现,拒付路径在生产从来没执行过——处理器在一个被误读的状态码上提前返回,而测试把网关 mock 成永远返回批准。断言确实跑了,但断言的东西跟业务一点关系也没有。

这就是幻觉问题:AI 代码看一眼就过,因为它在模式匹配"长得像能跑的代码";它的测试也过,因为测试确认的正是代码已经在做的事。评审挡不住这种东西。真正的门禁是测试,而测试本身需要证据。

我在每个 PR 上都要求的三件证据

这三件事合起来回答了唯一重要的那个问题:这些测试能不能在这个 diff 上区分正确与错误行为。光是 build 过了,什么都证明不了。

覆盖率表演以及如何识破它

行覆盖率是软件里被误用最多的指标。我审过 97% 行覆盖率但连除零都抓不到的套件。套路重复出现:

必须存在的四类测试

机器能校验的 Given/When/Then

我把验收标准固定写在 PR 描述里,格式如下,写不出来我就拒绝开始 review:

Given a customer with a valid card that the gateway will decline
  And an order in the pending state
When the payment handler processes the charge
Then the order moves to payment_failed
  And a decline email is queued
  And no capture request is recorded in the ledger
  And the response body contains the gateway decline code

四个 Then,四条断言,一一对应。如果验收测试文件里的断言数比 Then 少,那两边必有一边在撒谎。

当测试也是 AI 写的

助手有一切动机去写"刚好确认它自己写的代码"的测试。真正能用的防线只有几条:

评审清单与 CI 门禁

我的 reviewer 清单很短,每一条不过我就不批:

人会松懈,CI 不会。我的流水线会在这些情况下让 build 红:分支覆盖率下降、任何一行新的源码未被覆盖、源文件多了 50 行但测试文件没跟上、变更文件的变异分数掉到下限以下、或者 PR 描述里缺了指向 CI 运行 ID 的 Test-Evidence: 标签。最后那个门禁是我手里杠杆最大的一条——一个 bot 会在发现它指向某个三次 force-push 之前的陈旧运行时直接禁用合并。

Snapshot、flaky 以及其他谎言

Snapshot 测试是 AI 生成代码里最常见的覆盖率表演。助手调 toMatchSnapshot,第一次运行写下的 snapshot 就成了 oracle,从此这个测试永远确认组件第一次产出的那个结果,带 bug 也一起确认。Snapshot 只在有人逐行 review 每一处变更时才有用,所以我禁止用它测行为,只允许它用于纯格式 fixture。

Flaky 测试我是同一套粗暴政策:flaky 测试就是一次带着好 PR 的失败测试。一个工作日内修好或者删掉。永远不要 skip。被 skip 的测试会烂成"某个没人测的功能的文档",在绿色仪表盘上一点也看不见。如果那东西本质上就是概率性的,就在测试模式下把它变成确定的,或者挪到每晚跑的 soak 套件里单独报告。

我们强制推行之后发生了什么

头一个月挺难受。PR 吞吐量掉了下去,因为助手不停提那些过不了变异门禁的 PR。到了第三个月,通过我们的 prompt 和固定示例,助手已经学会在第一次就产出能在变异下存活的测试。那个拒付路径的 bug 没再出现过,因为"失败运行日志"这项要求在结构上就让"从没见过失败的测试套件"没法被合入主干。证据只要你去要就不贵。信任一旦丢了就很贵。

评审时看什么

这篇文章适合用在给 AI 代码设置测试证据门禁时。别从“原则”聊起,直接拿一条真实改动来对照,看看规格里还缺什么。

门禁的价值不是让流程变重,而是让评审者少猜一点:这段 AI 代码到底证明过什么。

证据门禁要看原始输出,不看截图

我更信一段完整命令输出,而不是一句“tests passed”。证据门禁最小可用版只有三样:命令、时间、完整输出。AI PR 如果只贴绿色截图,reviewer 看不到过滤条件、跳过用例和真实失败信息。

Evidence packet:
- command: pnpm test --filter api-contract --runInBand
- timestamp: 2026-04-28T09:40:12Z
- commit: 62f9d1e
- result: 38 passed, 0 skipped
- extra check: openapi-diff old.yaml new.yaml returned non-breaking
- missing: no load test; reviewer accepted because endpoint is read-only

边界:证据门禁不是让每个 PR 都跑全量回归。门禁应该和风险匹配。数据库迁移、队列消费、计费和认证要严;纯样式修复可以轻。

可复制产物:AI 编码评审包

在 AI 生成 diff 进入代码评审前使用。它把提示词范围、允许变更和证据要求合并成一个可审查产物。

AI 编码评审包:AI 编码的测试证据门禁:为什么通过用例还不够

本次要做的决策:
- 确认 AI 只在批准范围内生成变更,并为每条验收标准提供证据。

责任人检查:
- 产品责任人:
- 工程责任人:
- QA 或运维评审:

范围边界:
- 本次包含:
- 本次不包含:
- 仍需确认的假设:

验收证据:
- 测试或 fixture:
- 日志、指标或截图:
- 人工复核步骤:

AI 边界:生成变更必须留在书面范围内,每条验收标准都要能找到证据。

评审追问:
- 没参加需求会的人还会误解哪里?
- 哪个证据能证明这次改动足够安全,可以发布?

编辑复核记录

复核日期:2026-04-28。本次补充了可复用产物,按相关主题 Hub 检查了文章定位,并收紧下一步链接,让页面更像可操作参考,而不是孤立长文。

关键词:test evidence gates · AI 生成代码评审 · mutation testing · 覆盖率表演 · CI 合并门禁 · property-based testing · 验收标准

专题阅读路径

这篇文章归入 验收标准 主题。先读 Hub,再结合下面的清单、模板或工具落到具体项目里。

编辑说明与免责声明

最近复核:2026-04-28。编辑部检查了示例、内链和可复制评审片段,确保内容更适合真实项目使用。

本文用于软件工程教学与实践参考,不构成法律、税务或投资建议。示例场景用于解释规格方法,不对应真实客户数据。