规则工具、大模型、撤回历史修复实测对比:同一套测试,换一种造法,得分从 3% 变成 96%;真漏洞主要靠大模型造的变异体和撤回的历史修复找到
我们做了什么:变异测试是故意往代码里放假 bug(变异体),看现有测试能不能发现,没被发现的就是测试的漏洞。假 bug 有三种造法:规则工具机械地改符号,大模型先读测试再挑盲区下手,撤回历史修复把以前修好的 bug 放回去。我们拿同一套现有测试(Go 新代码 + Python 老代码),三种造法各跑一遍。
发现:
- 大模型造的变异体最能找到真漏洞:每 100 个里有 28 个(Go)和 39 个(Python)指出了真漏洞。
- 撤回历史修复得到的变异体,是唯一找回两个已知漏洞的;但代码一变,修复就很难撤回去。
- 规则工具在 Go 上造了 178 个变异体,只有 2 个指出了真漏洞。
- 同一套测试,对规则工具造的变异体得分 96%,对大模型造的变异体只有 3%–6%。测试没变,变的是变异体的难度。
- 两个变异测试工具都给过假结果,不处理会得出错误结论。
结论:想找测试的盲区,让大模型造变异体;想守住修过的 bug 不再坏,撤回历史修复当变异体;规则工具造变异体便宜,适合当底线。比较造法时数真漏洞,不要比分数;分数只适合在同一种造法下看前后变化。
先说清楚:变异测试是在检查测试
平时写的单元测试是保安,每天都在岗。变异测试是突击检查:故意往代码里放一个小 bug(叫「变异体」),看保安会不会发现。测试变红叫「杀死」,没变红叫「存活」。杀死的比例就是变异得分。
> 改成 >=变异得分(mutation score)= 被杀死的变异体数 ÷(变异体总数 − 等价变异体数)。它衡量的是测试集发现这批变异体的能力。
— 定义见 Jia & Harman 的变异测试综述 [18];该定义可追溯到 DeMillo、Lipton、Sayward 1978 年提出变异测试时的说法。
注意分母里是「这批变异体」。定义本身就说明了:分数是测试和变异体两方面一起决定的。研究也发现,扣掉测试集大小这个因素以后,变异得分和发现真实 bug 的能力只是弱相关 [2]。
本文的算法稍有不同:分母用的是「有效变异体」(能编译、能跑的),没有先扣掉等价变异体。原因是等价要逐个人工判定,我们只对存活的做了判定。所以表里的得分比按定义算的略低。比如 Go 规则组扣掉 3 个等价后是 171 ÷ 175 ≈ 98%。
变异测试本身不留在仓库里。它的价值是找出测试没守住的地方,然后你再补一条普通测试。
这次研究的起因是之前做的一个小试点:我们把 20 个已修 bug 的修复撤回去,看现有测试会不会报警。修复时带了测试的 15 个全部被抓住,没带测试的 4 个只抓住一半。那次的结论讲的其实是「普通测试有没有用」。这次我们把重点放回变异测试本身:造变异体的方法不同,结论会差多少?
三种造变异体的方法
还是那个比方:同一个保安,换三种人去试探他。
规则工具随手乱挪
工具按固定规则改代码:== 改成 !=,&& 改成 ||,continue 改成 break。量大、便宜、全自动。Go 用 gremlins,Python 用 mutmut。
大模型先看站位再下手
把代码和相关测试一起交给大模型,让它造「它认为现有测试抓不住」的真实型 bug,比如写错 ID、少个保护条件、状态记错。参考 2026 年 test-aware 变异的做法。每个文件 12 个。
撤回修复照旧案再偷一次
把历史上的 bug 修复反向打回去,得到的是一个真实发生过的 bug。打不上的(代码已经变了),交给大模型按意思移植成最小补丁。
实验怎么做
- Go 新代码:一个上下文引擎包里修 bug 最多的 3 个文件,约 1,230 行,2026 年 7 月才写的。包内测试跑一遍约 27 秒。
- Python 老代码:定时任务调度模块的 3 个文件,约 1,890 行,2025 年 12 月就有了,修过 30 次。相关测试 236 条,跑一遍约 5 秒。
- 基线选在补测试之前的提交,所以试点里发现的两个已知漏洞还在,可以当标准答案。
- 只认具名测试失败或测试超时算杀死;编译、导入失败记「无效」。
- 每个存活的变异体交给一个独立评审(另一个大模型 agent),它不知道变异体是谁造的、也不知道生成方的预测,分成三类:真漏洞、无关紧要、等价(行为没变)。
- 评审校准:把两个已知漏洞混在存活变异体里,不告诉评审。它两个都判成了「真漏洞,高置信」。
结果
先看形状:同一套测试,两张图里的线几乎上下颠倒。得分在「大模型造的变异体」这一格掉到谷底,真漏洞恰恰在这一格冲到最高。
分数低说明测试没抓住,没抓住的正是要找的盲区,所以两条线反着走。下表是完整数字。
| 组 | 有效 | 杀死 | 存活 | 真漏洞 | 无关紧要 | 等价 | 变异得分 | 每 100 个的真漏洞 |
|---|---|---|---|---|---|---|---|---|
| Go · 规则 | 178 | 171 | 7 | 2 | 2 | 3 | 96% | 1.1 |
| Go · 大模型 | 36 | 2 | 34 | 10 | 19 | 5 | 6% | 28 |
| Go · 撤回修复 | 10 | 8 | 2 | 2 | 0 | 0 | 80% | 20 |
| Py · 规则(抽样) | 106 | 59 | 47 | 6 | 29 | 12 | 56% | 5.7 |
| Py · 大模型 | 36 | 1 | 35 | 14 | 9 | 12 | 3% | 39 |
| Py · 撤回修复 | 19 | 18 | 1 | 1 | 0 | 0 | 95% | 5.3 |
Python 规则组是从 mutmut 造的 885 个里按文件各抽 40 个。Python 撤回修复组:30 个修复里自动撤回 3 个、移植 16 个,另 11 个牵涉已删除的代码,没有做。
存活的里面,有多少是真漏洞
已知漏洞找回了几个
| 方法 | 漏洞一:子代理记录证据时记错身份 | 漏洞二:删会话等锁没有上限 |
|---|---|---|
| 规则工具造的 | 没造出来 | 没造出来 |
| 大模型造的 | 不算数:大模型能读到仓库历史,看到这两处后来补了测试,故意避开了 | 同左 |
| 撤回修复造的 | ✓ 存活 | ✓ 存活 |
三种方法找到的是不是同一批漏洞
- Go:规则工具造的变异体指出的 2 个真漏洞,大模型造的变异体也都指出了;大模型造的另外指出 8 个规则工具没碰到的。撤回修复组的 2 个,另外两组都没有。去重后共 12 个。
- Python:规则组 6 个、大模型组 14 个,只有 1 处重叠。撤回修复组唯一存活的那个,和大模型造的某一个变异体是同一个改动。去重后共 19 个。
一句话:现有测试对规则工具造的变异体得分最高,但这组指出的真漏洞最少;对大模型造的变异体得分最低,这组指出的真漏洞却最多。变异得分只有在说明了「变异体从哪来」之后才有意义。
大模型造的变异体指出了哪些漏洞
挑几个评审判为高置信的,只描述行为。
| 改动 | 后果 |
|---|---|
| 中止判断里多加了一个「结果为空」的条件 | 被中止的定时任务又会被记成「成功」。代码注释里记着,这正是一次线上事故的形状。 |
| 执行锁按「任务」而不是按「员工」 | 同一个员工的两个定时任务会并发执行,违背文档约定的串行。 |
| 重新注册任务时不更新会话 ID | 改过会话的任务继续往旧会话发。撤回修复组也撞到了同一处。 |
| 证据工具去掉等锁的 3 秒上限 | 后台重建期间,一轮对话会一直卡着。 |
| 已核实的证据走进默认分支 | 模型被告知「已记为推测」,拿到错误引导。规则工具也翻出了这一处。 |
大模型造的变异体噪声也不少:Go 存活的 34 个里,19 个被判「无关紧要」,多是错误分支和极少走到的组合。所以大模型组离不开评审这一步,否则 34 个存活没法解读。
工具的坑:四种假结果
这一节比上面的数字更重要。每一种不处理,都会直接得出错误结论。
| 现象 | 看上去像 | 实际是 | 怎么防 |
|---|---|---|---|
gremlins 6 秒跑完 160 个,全部杀死 |
测试很强 | 它把代码复制到临时目录跑,go.mod 里用相对路径替换的本地依赖找不到,每次都编译失败,被算成杀死 |
耗时不合理就是假的。实验副本里把替换改成绝对路径 |
gremlins 报 19 个「没有覆盖」,不跑 |
这些行没测试 | switch 里 case 后面的条件不进 Go 覆盖率统计,这些行其实每次都跑。补跑后 11 个杀死、7 个存活 |
「没覆盖」的要看是不是 case 条件,必要时自己生成补丁跑 |
mutmut 2.5.1 跑了 291 个,全部存活 |
测试很弱 | 同一个变异体手动跑,测试是红的。问题在工具调度,不在测试 | 只用它造变异体,用自己验证过的脚本跑 |
| 自己的脚本把一个变异体判成存活 | 发现了新漏洞 | 测试卡死,跑满 10 分钟超时,输出里没有「测试失败」那一行 | 超时单独记一类,算作检测到 |
规则:全杀死和全存活一样可疑,先怀疑工具。只认具名测试失败;耗时、计数和日志对不上时,手动复现一个再说。
关于变异测试,学到了什么
成本
| 环节 | 大致量 |
|---|---|
| 规则工具 · Go 全量 | 约 3 分钟(2 个并行)。被杀死的大多很快就失败退出 |
| 规则工具 · Python 抽样 120 个 | 约 15 分钟 |
| 大模型造变异体 | 每种语言 36 个,约 16–18 万 token,4–6 分钟 |
| 移植撤不回去的修复 | Go 9 个约 15 万 token,Python 16 个约 17 万 token |
| 独立评审 | 每批存活约 11–15 万 token |
| 跑存活的变异体 | 存活的要跑满整套测试:Go 每个 30–50 秒,一次卡死跑满 10 分钟 |
评审的工作量和存活数成正比。规则工具在 Python 上抽 120 个就要判 47 个,其中大部分无关紧要。
怎么用到日常流程
- 只跑改动的文件,当便宜的底线。
- 注意
case条件的「没覆盖」和相对路径依赖这两个坑。
- 让大模型先看测试,再造 3–5 个变异体。
- 存活的交给独立评审,只把「真漏洞」给作者看。
- 当场撤掉修复,确认新测试会红。越晚越撤不回去。
- 定期抽查修复时没带测试的那些。
局限
- 每种语言只有 3 个文件;Python 规则组是抽样。
- 造变异体的和评审的是同一类模型,可能有共同的偏好;评审只用两个已知漏洞校准过。
- 造变异体的大模型能读到仓库历史,所以「已知漏洞召回」这一项对大模型组无效。下次要在看不到后续提交的代码上生成。
- 「真漏洞」是评审的判断,不是线上真的出过的 bug(撤回修复组除外)。
一句话:规则工具造的变异体适合当便宜的底线,大模型造的适合找新漏洞,撤回修复适合守住「修过的不再坏」。三者互补,不能互相替代。用任何一个之前,先确认工具给的结果是真的。
附:变异测试研究速览
做试点之前查过的资料,按问题归类,每条只留结论。
变异得分本身靠不靠谱
| 研究 | 结论 |
|---|---|
| Just 等,FSE 2014 [1] | 357 个真实 bug:排除覆盖率的影响后,杀死变异体的能力和发现真实 bug 的能力仍然显著相关。 |
| Papadakis 等,ICSE 2018 [2] | 控制住测试集大小后,这种相关性变弱。但测试集大小相同时,变异得分高的那组确实能发现更多真实 bug。所以变异体适合用来指导补测试,分数本身不是精确的质量刻度。 |
| Zhao 等,ISSTA 2026 [3] | 在大模型写的测试上复现:被测代码没有 bug 时,覆盖率和变异得分可以用来比较模型;被测代码已经有 bug 时,两者都不可靠。 |
工业界怎么用
| 来源 | 做法与结果 |
|---|---|
| Google [4][5] | 只在代码评审的改动上跑,跳过日志这类没意义的行,只把存活的变异体指给作者看,不给总分。研究分析了近 1500 万个变异体:用了变异测试的开发者写的测试更多、质量更好;回看高优先级的真实 bug,如果当时跑过变异测试,会有一个存活的变异体提前报警。 |
| Meta ACH,FSE 2025 [6] | 针对隐私问题生成变异体,再让大模型写测试去杀死它们。工程师接受了 73% 的测试;571 个测试里有 277 个,如果只看行覆盖率会被丢掉。 |
用大模型造变异体
| 研究 | 结论 |
|---|---|
| LLM 变异测试综合研究 [7] | 大模型造的变异体更像真实 bug,检出率约 77%,规则工具约 42%。代价是编译不过、重复和等价的变异体都更多。 |
| SMART,FSE 2026 [8] | 从约 13 万对真实修复里检索例子再生成:有效生成率从 42.89% 升到 65.6%,真实 bug 检出率 92.61%;7B 小模型加上这个方法,可以追平或超过 GPT-4o。 |
| Test-aware 变异,ESEM 2026 [9] | 把现有测试也交给大模型,让它造一个能骗过这些测试的 bug。骗过现有测试、又被完整测试抓住的比例:HumanEval 87.7%,MBPP 79.1%;同条件下 mutmut 只有 4.4% 和 5.7%。 |
大模型写的测试质量如何
| 研究 | 结论 |
|---|---|
| 单元测试生成综述 [10] | 引用的结果:大模型写的测试变异得分最高 0.546,人写的是 0.690;有的研究里接近 0,因为测的是接口、空方法这类没有逻辑的地方。 |
| 软件演化研究,2026 [11] | 8 个模型、22,374 个程序变体:代码语义改变后,新测试通过率降到 66%;失败的测试里超过 99% 在旧代码上能通过。 |
| Python 类级测试,2026 [12] | 覆盖率几乎都到顶,分不出好坏,变异得分能拉开差距;选哪个模型比怎么写提示词影响更大。 |
| SWE-Mutation,2026 [13] | 最强的模型检出率只有 36.15%。但评审指出这些变异体没有和真实 bug 对照过,结论还不能算定论。 |
把存活的变异体交回给大模型
| 研究 | 结论 |
|---|---|
| MuTAP [14] | 把存活的变异体写进提示词,让模型重写测试,HumanEval 上变异得分约 94%。只在小基准上验证过。 |
| AdverTest,2026 [15] | 一个 agent 写测试,一个 agent 造变异体,两边对抗。Defects4J 上缺陷检出率比最好的 LLM 方法高 8.56%,比 EvoSuite 高 63.30%。 |
| Critic 循环实验,2026 [16] | 迭代修改能杀掉初始测试漏掉的变异体中的 78%。作者还发现,自己早先一个极显著的结果其实是模型输出被截断造成的。评测工具本身也会出错。 |
| 实践文章,2026-08 [17] | 四个项目都没改生产代码,变异得分全部上升。其中一个解析器行覆盖率 100%,变异得分只有 61.29%。作者的建议:不要让 agent 去优化分数;把存活的变异体当作反例,写一个在原代码上通过、在变异代码上失败的测试;同时把需求交给 agent。 |
一句话:变异测试适合用来指出测试漏在哪里,不适合拿来打分。大模型造变异体已经比规则工具更像真实 bug,但它写的测试容易照着现有代码写断言。最好的用法是让大模型出题,测试是否真的有效要另外验证。
参考资料
- Just et al. Are mutants a valid substitute for real faults in software testing? FSE 2014.
- Papadakis et al. Are Mutation Scores Correlated with Real Fault Detection? ICSE 2018.
- Zhao, Zhou, Cohen. Do Coverage and Mutation Scores of LLM-Generated Test Suites Correlate with Their Effectiveness? ISSTA 2026.
- Petrović, Ivanković. State of Mutation Testing at Google. ICSE-SEIP 2018.
- Petrović et al. Does mutation testing improve testing practices? ICSE 2021.
- Foster et al. Mutation-Guided LLM-based Test Generation at Meta. FSE 2025 Industry.
- A Comprehensive Study on Large Language Models for Mutation Testing. arXiv 2406.09843.
- Wang et al. Boosting LLMs for Mutation Generation (SMART). FSE 2026.
- Kiele et al. Beyond Rule-Based Mutation Testing: Test-Aware Mutant Generation Using LLMs. ESEM 2026.
- Large Language Models for Unit Test Generation: Achievements, Challenges, and the Road Ahead. arXiv 2511.21382.
- Haroon, Khan, Gulzar. Evaluating LLM-Based Test Generation Under Software Evolution. arXiv 2603.23443.
- Al-Ahmad et al. Evaluating the effectiveness of class-level LLM-generated test suites in Python. arXiv 2609.24341.
- Sun et al. SWE-Mutation: Can LLMs Generate Reliable Test Suites in Software Engineering? arXiv 2605.22175.
- Dakhel et al. Effective Test Generation Using Pre-trained LLMs and Mutation Testing (MuTAP). arXiv 2308.16557.
- Chang et al. Test vs Mutant: Adversarial LLM Agents for Robust Unit Test Generation. arXiv 2602.08146.
- Otterson. Adversarial Test-Hardening for AI-Written Code. arXiv 2607.23002.
- Mutation Testing for Agent-Written Code. awesome-testing.com, 2026-08.
- Jia, Harman. An Analysis and Survey of the Development of Mutation Testing. IEEE Transactions on Software Engineering 37(5), 2011.