CI 一排绿钩子,覆盖率 91%,你点下 merge,心里踏实得不行,觉得自己是个负责任的工程师。
那批测试是 AI 写的。
——这两句我建议并排放着读,后一句是前一句的验尸报告。
上周 arXiv 挂了一篇论文,标题一点不客气:TestSuiteBench: Coding Agents Struggle to Generate Strong Test Suites from Specifications。人话版本:你把规格说明丢给 agent,让它自己写一整套测试,它不太行。
165 个开源应用,每个配了规格、参考实现、参考测试套件,外带平均 74 个变异体。变异体这玩意儿说白了就是把代码故意改坏一点点——改个判断、翻个边界——然后看你的测试抓不抓得住。
抓住了,说明这条需求有人看守;抓不住,说明这条需求在测试套件里是无人区,随便谁都能进去溜达。
结果:前沿 agent 的变异得分,低于 60%。
抓不住四成。
你请了一队保安,西装革履,站姿标准,交接班记录写得比员工手册还工整。坏人从他们面前走过去,四个里快两个没反应。
【灯光渐暗,保安队伍里有人开始鼓掌】
论文还给了个更妙的数字:这些测试套件里,88% 的用例能跑通参考实现。
也就是说,保安不但不抓人,还全勤!
我想说的是这个。测试通过率高,不是好消息,是个信号。
一个套件失败率高,你会去修它。一个套件 100% 通过,你不会多看一眼——你只会想“嗯,稳”。恰恰是这种稳,让 agent 写的测试变成了最难被发现的那一类债。
烂代码会喊叫。烂测试不喊叫,它安静得像个模范员工。
论文里还有一行我盯着看了半天:参考测试套件里,73% 的变异体只被一个测试杀死,平均只被其余测试的 0.2% 杀死。
0.2%。这套测试几乎没有冗余。每个测试都是专属守夜人,只负责一条需求、一个边界、一种输入组合,谁都不越界,谁都不替谁代班。一人一份工,清清楚楚。
这是高手写测试的样子。
而 agent 写出来的套件,论文说规模偏小,还“缺少相关的输入组合”。翻一下:它把同一条快乐路径测了八遍。八个函数,名字一个比一个讲究,都在传 input = "valid_value",都在断 assert result is not None。剩下的空值、极值、边界、零条和第 n+1 条,全空着。
你看着这八个测试,会产生一种“我被覆盖了”的幻觉。
其实你只是被围观了。
同一个规格说明,让它写实现,能通过对方 82% 的参考测试;让它写测试,只能抓住对方不到六成的变异体。它答题的水平,远高于它出题的水平。论文由此得出一个很重的结论,说 agent 能构建软件,却验证不了自己构建的软件,全自主开发这条路直接卡死了。这话写得很学术。换成打工人的说法:全自动开发的瓶颈,从来不在“造得出”,在“验不下去”。
我怀疑大部分人对 agent 写测试的态度,都走过三个阶段。
第一阶段,质疑。你不信它,每行测试都扫一遍,发现有一半根本没断言到点子上,手动补,补到怀疑人生。
第二阶段,接受。你懒得看了,绿就行。你甚至会在 PR 描述里写“新增测试用例,覆盖核心逻辑”——自己都不太信。
第三阶段,依赖。你开始只让它写测试,因为它快,而且它从不说累。
第三阶段最危险,因为你已经不看测试内容了。你只是收藏了那个绿。
——行,跑偏了,说回那个 88%。我真正在意的不是 60%,是 88%。是它生成的测试太有“完成感”:格式对、命名规范、缩进漂亮、能跑、全过。完成感是审查的麻醉剂。一份看起来完成的测试套件,比一份明显残缺的测试套件更能骗过你,残缺的那个至少还能逼你动手。
这里我得说句实话,不然显得我在装。我自己写的测试也不干净:happy path 写得飞快,边界用例永远挂在 todo 里,等线上真炸了,回头补一个只有一行 assert 的回归测试,commit message 写个“fix”,然后把它推出去当哨兵——哨兵这辈子只喊过一次救命。
agent 干的不过是把这套动作重复了一万次,更快,脸皮更厚,连心虚都省了。
所以那篇论文没让我焦虑“AI 会不会取代我”。它让我重新看了一遍自己 CI 首页上那些绿钩子,然后产生一种很具体的不适:我不知道那堆测试里,到底有几个是真的在看门,有几个只是站那儿维持秩序。
(友情提示:本文不构成对任何具体模型或工具的评测,只构成对我个人 code review 态度的重新评估。你要是昨晚也是盯着一排绿钩子睡着的,评论区扣个“同款”,咱一起失眠 🫠)
