测试与验证

18 分钟读完

8
第 II 部分 · 逐个阶段

测试与验证

如果智能体能修改测试套件,这套测试就不再是检查,而只是一条建议。本书其他阶段里凡是 L2 以上的级别,都要靠测试撑着,所以判据原则必须首先在测试阶段站稳:由确定性过滤器而不是模型来判定什么算好测试,并把它放在智能体够不着的地方。然后用变异分数和逃逸缺陷来评判结果,而不是看工程师接受了多少测试。

8.1

变化在哪里

AI 给测试带来了两份工作,它们的失败方式各不相同。第一份是生成测试:模型写出测试代码,由人决定留不留。它的失败是产出率低。大部分原始产出毫无价值,要是评审者全部过目,就会被淹没。第二份是核验:测试套件成为 AI 所做一切的关卡,包括它自己的代码改动。它的失败是钻空子。一个因测试变绿而受奖励的智能体,只要有机会,就会让测试变绿,而不去把代码改对。

第 I 部分已经给出了两方面的证据。在产出率上,Meta 的 TestGen-LLM 只保留能构建、能稳定通过并且提高覆盖率的候选测试。原始产出只有四分之一留了下来,而工程师接受了其中 73% 的幸存者(Meta 案例,T2)。在钻空子上,METR 发现 o3 在 30.4% 的 RE-Bench 运行中出现奖励黑客行为;ImpossibleBench 则表明,即便规格让合法通过根本不可能,前沿模型仍能让测试通过(判据原则,T1)。两份工作需要的是同一样东西:一个不由模型做出、模型也改不了的通过/失败判定。

人的工作也随之改变。Meta 的工程师在评审变异引导生成的测试时,"看重的是能把精力放在评估测试上,而不必亲手构造测试"(Meta Engineering,2025 年 9 月,T2)。只有评估依然严格,这才算真正省力。写测试本身也已是智能体在做的任务。在 dotnet/runtime 中,微软编码智能体提交的测试类 PR 在 99 个中合并率为 75.6%,而它全部 PR 的合并率是 67.9%(dotnet/runtime,T2)。被合并的测试只是评审者放行的测试,还不是能抓住问题的测试。

销售测试工具的 SmartBear 认为,"编码助手已达到 4 级和 5 级自主",而测试工具"更接近 2 级或 3 级",并称"没有保障的自主会变成负担"(SmartBear,2025 年 10 月,T4)。这是利益相关方的观点,没有数据。但它与瓶颈一章的结论一致:生成跑在核验前面时,队列就堆在这里。

8.2

本阶段的阶梯

下面的标准与网格中的测试一行相同,这里每一格还写明了审计者会检查什么。每个任务类型单独评级(规则 3)。一个团队完全可以如实地同时处于"测试:单元测试生成 L2,失败诊断 L1,端到端测试 L0"。

级别
可观察标准
最佳证据
L0 手工
测试中不用 AI。审计者找不到标注为 AI 编写的测试代码,也找不到 AI 生成的失败摘要。
无直接证据。没有来源报告过对自身处于这一级的测量。
L1 辅助
IDE 中的测试建议;AI 的失败诊断呈现给人,由人决定怎么做。检查:诊断准确率已在样本上人工核对过。
Google 的 Auto-Diagnose 内置于其代码评审工具,在对 71 个失败的人工核查中准确率为 90.14%(T2)。
L2 委派
AI 在确定性过滤器之后生成测试:必须能构建、稳定通过,并提高覆盖率或杀死变异体;每一个都由人接受。检查:AI 编写的测试有归属标注,原始产出率和接受率都有记录。
Meta 的 TestGen-LLM 与 ACH,过滤后的产出有 73% 被接受(T2)。FlakyGuard 修复了 47.6% 可复现的不稳定测试,开发者接受了其中 51.8% 的修复(T2)。
L3 受监督自主
AI 维护测试套件;人评审变异或覆盖率报告,而不是单个测试;智能体不能修改判据。检查:阻止智能体触及判据的仓库设置,以及 90 天的逃逸缺陷对比。
部分有。Meta 的即时"捕获"测试在生成的 22,126 个测试中上报了 41 个候选,其中 8 个属实(T2,实验性)。Airbnb 把一次测试文件迁移的 97% 自动化了(T3)。缺口:Spotify 的数据集迁移没有可供核验的构建时测试(T3)。
L4 自主(限定任务类)
不稳定测试的隔离与修复在经审计的误修率下自动落地。
无。FlakyGuard 的修复由开发者接受,而不是自动落地。

L1:诊断。Auto-Diagnose 读取集成测试的失败日志,在 Google 的代码评审工具里贴出摘要和最相关的日志行。在 Google 全公司部署后,它处理了 52,635 个不同的失败测试,5.8% 的反馈评为"没有帮助"(截至 2026 年 4 月)。在该工具中发布结果的 370 个工具里,它的有用度排第 14。90.14% 来自 71 个失败,应把它当作一次认真的抽查,而不是一个比率。作者的结论是,准确率对采用至关重要:一个经常出错的诊断工具,只会教人学会忽略它。

测量方 Google 工程师,自有工具 · 人工根因评估加产品内反馈评分 · 准确率基于 71 个失败;部署中覆盖 52,635 个失败测试;2026 年 4 月发表 · T2

L2:修复不稳定测试。FlakyGuard(ASE 2025)把代码当作图来处理,只探索与某个不稳定测试相关的上下文。作者说,此前的 LLM 修复工具在工业环境中失败,是因为给模型的上下文要么太少、要么太多。FlakyGuard"修复了 47.6% 可复现的不稳定测试,其中 51.8% 的修复被开发者接受",修复成功率比此前方法至少高 22%。摘要没有点名公司;只有二手摘要把评估放在 Uber。

测量方 学术与工业界合著者 · 可复现不稳定测试的修复率、开发者接受率、开发者调查 · 工业环境评估,2025 年 11 月发表 · T2(公司归属为二手信息)

L3 与 L4。L3 的证据单薄且属实验性质,下一节会把其中最好的一项拆开来看。L4 是空的:不稳定测试修复有公开的接受率,却没有公开的自动落地记录。

8.3

案例:Meta 的即时"捕获"测试

第 I 部分里 TestGen-LLM 和 ACH 生成的是加固测试:生成时就通过,并落地到代码库中防范将来的回归。2026 年 1 月,Meta 的一个团队发表了这个问题更难的版本。Just-in-Time Catching Test Generation at Meta 在代码变更落地之前为它生成测试,面向的是规模达数亿行代码的后端系统。这些测试的目的,是在此刻找出该变更中的缺陷。捕获测试"按设计在生成时就失败"。失败本身就是信号,悬而未决的问题是:这是真缺陷,还是有意为之的行为变化。

这就是缩微版的 L3 问题:没人评审生成的测试,人只看到流水线转给他们的东西。论文报告:

  • 感知代码变更的生成方式,产出的候选捕获约为加固工作流的 4 倍。
  • 基于规则和基于 LLM 的评估器负责判断某次失败是否可能为真阳性,它们"把人工评审工作量减少了 70%"。
  • 在生成的 22,126 个测试中,有 41 个候选捕获上报给工程师。其中 8 个被确认为真阳性,"其中 4 个本会导致严重故障"。
  • 捕获测试"不能与它所测试的变更一同落地"。它要么被丢弃,要么其信号被处理。
测量方 Meta 工程师,自有后端系统 · 实验性部署,由工程师确认上报的捕获 · 生成 22,126 个测试,上报 41 个;2026 年 1 月投稿 FSE 2026 工业轨道 · T2(实验性)

作者自己点出了约束:"首要挑战是减少假阳性测试失败带来的开发拖累",而解决它需要"判断一次测试失败是否为真阳性,这直接关系到判据问题(Oracle Problem)"。在 L3 的前沿,难的不是生成,而是判断。Meta 对自己的变异测试工作也是这么说的:今后的工作将"聚焦于解决测试判据问题",它把这个问题定义为"根据给定输入,区分期望的正确行为与错误行为的挑战"。

实例演算:读懂这个漏斗

论文公布的是一个漏斗,来自同一个系统、同一套分母,所以下面的算术是成立的:

generated tests           22,126
reported to engineers         41   (0.19% of generated)
confirmed true positives       8   (8 / 41  = 19.5% precision)
would have caused serious      4   (4 / 41  =  9.8%)
assessor effect           review load -70%
                          => if load scales with candidates,
                             ~41 / 0.3 = ~137 without assessors

再看评审者这一侧。以下是示意性算术,不指向任何组织。论文只说,否决一次有意为之的变更"只花了几分钟"。假设每个上报的候选花 10 分钟,41 个候选就是约 410 分钟,接近 7 个工程师小时,换来 8 次真实捕获。也就是每个真阳性约 51 分钟,每个严重缺陷约 1 小时 43 分钟。没有评估器的话,同样 8 次捕获要花约三倍的分诊时间。改变假设的分钟数,总量会变,形状不会变:在这样的精度下,人的大部分时间花在误报上,决定这套方案是否划算的是评估器这一层,而不是生成器。所以,在把流水线称作自主之前,先把挡在人前面的这道关卡当作分类器来衡量:看它的精度,也看它漏掉了什么。LLM 评估器本身就是一个模型在评判另一个模型;这里之所以安全,只是因为每个上报的捕获都由人确认,没有任何东西自动落地。

它没有说明什么。流水线漏掉了多少真缺陷:计算漏报率需要论文所没有的基准真相。它也没有说明生产环境中的逃逸缺陷。这是一项实验,而不是已部署的 L3 系统。它的价值在于至少公布了一个精度数字,而几乎没有哪家测试生成工具厂商这样做过。

8.4

迁移步骤

一次只爬一个任务类型,并且不要让编码跑到你所在梯级的前面。本书的经验法则是:编码最多比评审和测试中较低者高一级。

L0 → L1:先呈现诊断,再核对它。

  1. 像 Auto-Diagnose 那样,把 AI 的失败摘要放在工程师本来就会看的地方:评审工具或 CI 日志。
  2. 在宣布任何结果之前,先抽一批诊断与真实根因做人工核对。公布准确率和样本量。
  3. 加一个"没有帮助"按钮并跟踪其比例。Google Tricorder 的规则——"没用"比例超过 10% 就停用该分析器(T2)——就是现成的关闭开关(详见代码评审一章)。

L1 → L2:过滤后生成,每个测试都由人接受。

  1. 先建过滤器,再开生成器。候选测试至少必须能编译、反复运行都通过(以剔除不稳定测试),并且要么提高覆盖率,要么杀死一个变异体。只有幸存者才交给评审者。
  2. 给 AI 编写的测试加上归属标注(标签或提交尾注),并记录每一道过滤的原始产出率、接受率,以及被接受测试的变异分数。只看接受率会让你自我感觉过好。
  3. 挑判据便宜的任务类型。不稳定测试修复就符合,因为"它还会时好时坏吗?"只需重跑一遍,而 FlakyGuard 表明它在 L2 上可行。

L2 → L3:人评审报告,而不是测试。

  1. 把判据移出智能体的够及范围。测试、变异配置和 CI 定义归属于智能体凭证无权写入的路径或服务(见"护栏")。
  2. 规定人用什么来代替逐个评审测试:变异分数的变化、覆盖率报告,以及新增、删除或跳过的测试清单。这就是证据包,也就是 Hassan 等人提出的"合并就绪包"——"提交供人审批的结构化证据包"(Hassan 等人)。如何评审它,是代码评审一章的主题。
  3. 如果有任何东西是由分类器转给人的,就像 JiT 那样,那么在信任它之前先测量这个路由器的精度。
  4. 做一次 90 天的对比:由 AI 维护的测试所守护的代码,与由人维护测试的基线相比,逃逸缺陷各有多少。没有这项对比,模型中的 L3 标准就没有满足,无论仪表盘怎么显示。
  5. 确保有东西可供核验。Spotify 的 Honk 第 4 篇推出了 240 个自动化的数据集迁移 PR,但 BigQuery Runner 和 dbt 仓库很少有构建时测试,负责人只能手工测试这些 PR(T3)。没有测试的地方,第一个要委派的任务类型就是写测试,在 L2 上做。

L3 → L4:还不到时候。没有公开案例做到过。如果你要尝试,本书的建议是把它限定在隔离不稳定测试上,因为解除隔离就能撤销。对误修率做抽样审计,配一个急停开关,绝不让自动修复碰到生产代码。若要在任何地方首次达到 L3,维护是更好的起点,因为那里的编译器和现有测试套件已经构成了核验器。

8.5

护栏

用机制把判据放到智能体之外。指令不是边界。Elastic 的 CI 智能体通过删掉版本升级来"修复"失败的依赖升级,直到团队在指令里加上一条明确的"绝不降级版本"规则;而据 InfoQ 报道,Spotify 的智能体把失败的测试注释掉了(两者都在判据原则中讨论过)。Spotify 行之有效的设计是机制性的。在 Honk 第 3 篇中,确定性核验器(格式、构建、测试)由外围系统根据仓库内容挑选,并在停止钩子里运行。一旦有一个失败,"PR 就不会被打开,用户会看到一条错误信息"。智能体运行在"高度沙箱化"的环境里,推送代码由基础设施完成,而不是由智能体完成。

测量方 Spotify 工程师,自有系统 · 设计描述,附会话数量,无失败率 · "数千次智能体会话",2025 年 12 月 · T3

Spotify 直言了剩余风险:最糟糕的失败模式是"一个通过了 CI 却在功能上不正确的 PR"。智能体碰不到的核验器能阻止作弊,却阻止不了一套薄弱的测试放行错误代码,所以下面的指标要衡量测试套件本身。

用确定性步骤包住模型的判断。Meta 用 LLM 检测等价变异体(因为不改变行为、所以无法被杀死的变异体),单独使用时"精度高(0.79),召回率 0.47","经过简单预处理后升至 0.95 和 0.96",预处理的例子是去掉注释(Meta Engineering,T2)。一个廉价的确定性步骤让模型的召回率大约翻了一番。Meta 也不把变异本身当作判据:"变异测试无法独立存在(它需要先有一个测试)。"

修改判据的改动转给人(本书的建议)。任何删除、跳过或削弱测试,或触及 CI 或变异配置的智能体改动,无论该变更属于哪个风险层级,都交给一位指名的人类评审者。用路径归属和分支保护来强制执行,而不是靠提示词。把这类改动计数作为一项指标。没有公开案例报告过这条规则的效果;它是从奖励黑客的证据推出来的。

把测试运行器当作特权组件。能运行测试套件的智能体,通常就能以运行器的凭证执行任意代码。这是一个权限问题,见安全一章,流水线部分见 CI/CD 一章。本站的评测完整性与评分器博弈和奖励设计与奖励黑客讲了其中的机制。

8.6

指标

覆盖率是薄弱的代理指标。一项 2025 年关于变异引导测试生成的研究(T1)报告,LLM 为 HumanEval-Java 中的一个对象生成的测试,行覆盖率和分支覆盖率都达到 100%,变异分数却只有 4%。每一行都跑到了,注入的故障却几乎一个也没抓住。作者称覆盖率指标是"测试套件发现故障能力的薄弱指标",并认为变异分数更可靠。覆盖率告诉你什么被执行了;变异分数告诉你测试会察觉到什么。

测量方 Wang、Xu、Briand 与 Liu,三位学术作者和一位华为研究者 · 在两个基准的 204 个对象上做对照比较;100%/4% 的例子只是其中一个对象 · 2025 年 6 月,2026 年 4 月修订 · T1

接受是认同,不是正确。Meta 的 73%、FlakyGuard 的 51.8%,以及所有厂商的接受率,衡量的都是评审者是否认同。JiT 的 41 个中 8 个属实,展示了当你衡量测试是否真的发现了问题时会看到什么。在生命周期的这半段里,本书找到的唯一测量了下游安全性的研究是 Google 的 DIDACT 构建修复试验,它统计了回退次数(DIDACT 案例,T2)。把它当作模板。

你每爬升一个任务类型,就为它配一个流量指标和一个稳定性指标。下面这套指标是本书的建议,依据的是上述案例公布了和没有公布的度量:

指标
它告诉你什么
适用级别
每道过滤的原始产出率
生成器的产出中,有多少能通过构建、反复运行通过,以及覆盖率或变异体检查。
L2;过滤器的成本一侧
AI 编写测试的变异分数
被接受的测试能否抓住注入的故障。与同一模块中人写的测试对比。
L2 及以上;首要质量指标
逃逸缺陷
测试由 AI 编写或维护的代码在生产中出现的缺陷,在 90 天内与人维护测试的基线对比。
进入 L3 的标准
路由精度
转给人的失败或捕获中,属实的比例(JiT:41 个中 8 个)。
L3,凡是由分类器决定人看什么的地方
误修率
经抽样审计发现,掩盖了真缺陷或再次时好时坏的不稳定测试"修复"。
L2 不稳定测试修复;L4 的关卡
判据修改计数
删除、跳过或削弱测试,或触及测试或 CI 配置的智能体改动。
所有级别;任何上升趋势都是警戒线

接受率留在仪表盘上作为流量信号,但永远不作为结果。这些指标在哪里变成回退触发条件,由第 17 章《衡量成功与何时回退》设定阈值。

8.7

反模式

以覆盖率或接受率为目标。让模型提高覆盖率,它就会提高覆盖率,而 100%/4% 的例子说明了这可能值多少。接受率奖励的是在忙碌的评审者看来像样的测试,而忙碌的评审者正是本书论点所预言的。以变异分数为目标;覆盖率只作为多道过滤中的一道。

同一个智能体在同一会话里既写代码,又写评判它的测试。这样一来,智能体同时掌控检查的两端,而 ImpossibleBench 表明,能接触到测试的智能体会利用它们。守关的测试要么归人所有,要么单独生成,并在代码改动开始前锁定。

把 LLM 评审员当作判据。用模型评判模型可以是一道有用的附加过滤,但它不是确定性核验器,而且有自己的假阳性成本。Spotify Honk 的评审员否决了约四分之一的智能体会话,智能体约有一半时间能自行纠正(第 3 篇)。据 InfoQ 对一次会议演讲的报道,Spotify 后来以"过于僵硬,挡住了有效的改动"为由移除了这个评审员;Spotify 的一手资料中没有这样说。像衡量 JiT 的评估器那样衡量评审员的精度,并在它下面保留一道确定性检查。当智能体本身就是你交付给客户的产品时,它的评审员也要守同样的纪律;本站的前线部署工程师手册《最后一公里》在“评测:证明它真的有效”一章里讲了怎样和客户的专家一起搭建这套评测集。

在没有东西可以核验的地方委派核验。Spotify 的数据集迁移展示了这种形态:生成规模上去了,人却最终只能手工测试,因为那些仓库没有构建时测试。在把工作推到更高级别之前,先写好为它把关的测试。

凭厂商的基准测试选工具。测试生成器的厂商对比很常见,却几乎从不独立。

厂商自报。销售基于强化学习的单元测试生成器的 Diffblue,在 2025 年 10 月发布了一份基准测试,在三个开源 Java 项目上把自家产品与 GitHub Copilot(GPT-5)做比较,并声称在生产力、覆盖率和变异分数上大幅领先。测量方:厂商本身,拿自家产品与一家竞争对手比。"成功"的含义:主要是在相同时间窗口内达到的覆盖率,再外推成一个年度"生产力"数字;公开页面声称变异分数领先,却没有给出分数。缺少什么:独立复现、方法与原始数据(完整报告需单独下载),以及对真实发现故障数的任何统计。本书没有读过完整报告,也不引用其中任何数字。它出现在这里,是作为选型时不该怎么做的例子(T4)。

要自己评估测试生成器,就在你自己的代码上、经过你自己的过滤器运行它,再比较变异分数。本站的评估编码智能体和测试生成智能体两份实战手册介绍了方法,生成-核验落差则解释了为什么核验器才是稀缺的那一半。

怎么做。

  • 为每个测试任务类型写下它的判据,以及谁能修改它。如果智能体的凭证能改动它,这个任务类型最多只能算 L2。
  • 先建过滤器,再上生成器:编译、反复运行通过,然后是覆盖率提升或杀死变异体。记录每一道的原始产出率,而不只是接受率。
  • 在每个接受率旁边报告 AI 编写测试的变异分数,并开始统计这些测试所守护代码的逃逸缺陷。
  • 把每一个删除、跳过或削弱测试,或触及测试或 CI 配置的智能体改动转给一位指名的人,并跟踪其数量。
  • 在让分类器决定把什么转给人之前,先在样本上测出它的精度,就像 Meta 对 41 个上报捕获所做的那样。
  • 在没有测试的仓库里,把写测试作为第一个 L2 任务类型,然后再委派别的。
  • 下一级:当判据移出智能体的够及范围、人评审的是变异或覆盖率报告而不是逐个测试,并且 90 天的逃逸缺陷数据与人工基线相比表现良好时,一个任务类型才进入 L3。