AI 代码狂飙后,测试成了新瓶颈
Gangqiang Yao,Pi Agent contributor、研发团队负责人。他的团队一天上线 50 次、一天至少跑 300 轮测试。这个三连发系列用一线数据回答了一个 vibe coding 时代所有人都撞上的问题:代码产出提速 10 倍之后,为什么交付反而更慢了?
这篇文章回答的问题:AI 写代码飞快之后,测试怎么写才不碎、怎么跑才不贵?
这篇文章应该回答但没回答的问题:他的团队多大规模、投入了几个人维护这套测试基建——照抄之前你得先掂量这个。
系列全貌:一条论证链
这不是三条孤立的技巧帖,是一套完整的论证:测试保不了质量(破)→ 但测试仍是地基,要写对(立)→ 写对了还不够快不够省,要工程化(用)。作者的终局预告是:接下来聊 AI 写的代码该怎么 review。
质量悖论:vibe coding 没有让项目变好
「写代码一直是最容易的环节,AI 只是让最容易的环节更容易了。」测试从来不是高质量软件的关键——Quality is designed in, not tested in。
怎么写测试:六条军规
测试行为不测试实现、BDD 先行、禁止 mock、E2E 优先、覆盖率看剩下那 10%、规则全部 lint 化。系列中最火的一条,73K 浏览。
怎么跑测试:300 轮/天的降本工程
精准测试(文件级影响反查)、测试分级(核心路径高频跑)、Runner 调度(本地 runner 比 GitHub 托管便宜不少)。附两篇顶会论文。
第一幕:质量悖论
开篇立论,22.8K+ 浏览。他从两个现场观察切入,最后落到一句软件工程的老话上。
观察一:vibe coding 产出大量代码,大部分项目质量并没有提高。他甚至听到某创业公司的人说——
「我觉得可能人肉写写代码,稍微长期来看,速度更快,因为现在天天修 bug。」
观察二:很多人说「那你让 AI 写测试啊」。现实是,如果不做任何调教,AI 写出来的全都是充满 mock 的、测试实现细节的脆弱测试,而不是验证行为的测试。
更残酷的是:即使你教会了 AI 写对测试,照样不能保证软件的质量。因为——
测试从来不是高质量软件的关键。Quality is designed in, not tested in.
这句话化用自质量大师 W. Edwards Deming 的经典论断。测试只能证明缺陷存在,不能证明缺陷缺席;质量是在设计和需求澄清阶段注入的,不是在测试阶段补出来的。他的潜台词:天天修 bug 的团队,短板不在写代码,在需求澄清和验证这两环——AI 把写代码压到接近零之后,这两块短板彻底暴露了。
第二幕:写测试的六条军规
系列最火的一条(73K 浏览、268 赞)。前提明确:测试不是质量的关键,但它是基础。针对业务项目,怎么写才算写对——六条,每条都直指 AI 写测试的惯性毛病。
测试行为,不测试实现
测「重复下单不会重复扣款」,不测「某个方法被调用了几次」。业务没变,重构一下测试就碎一地,那多半写错了。AI 特别容易写这种测试——因为它写完实现顺手照着实现生成测试。
BDD 先行,场景即合同
先写验收场景再写代码,测试描述统一用 user 开头,Given / When / Then 三段式,用户和 PM 也能看懂。核心是防循环论证:别让 AI 写完代码,再照着自己的实现证明自己没错。
禁止 mock
Redis、PostgreSQL 能用真的就用真的;外部系统不好接就注入 fake,但要校验 fake 和真实系统的契约。时间逻辑用 fake timer,别 sleep 几秒再祈祷。AI 真的太喜欢随手 mock 了。
E2E 优先,宁可少而真
大部分测试从用户入口走到可见结果,外部依赖可以 fake,实在不合适再写其他测试。测试多了就分级 + 精准测试 + 定期完整跑。别改两行代码,等 CI 半天。
覆盖率 90%,重点在剩下的 10%
分支覆盖率先定 90%,但重点是剩下的为什么没测——尤其是失败路径。别让 AI 为了凑数字再写一堆垃圾测试。数字好看但失败路径全裸奔,等于没测。
规则全部 lint 化
前面的规则尽量都写成 lint——不靠人的自觉,不靠 code review 记性,靠工具强制。AI 写测试的反模式(mock 泛滥、测实现细节、凑覆盖率)都逃不过静态检查。
注意六条之间的咬合关系:1 和 2 解决「测什么」(行为契约),3 和 4 解决「怎么测」(真实依赖、走完整链路),5 和 6 解决「怎么守住」(覆盖失败路径 + 工具强制)。这是一套体系,不是六个tips。
第三幕:300 轮 CI 怎么跑得快、跑得省
前两条评论区被问爆的问题(@yetone 也问了):vibe coding 代码产出快了,测试慢、CI 贵的问题被放大。他的答案是一个工程组合拳,19.5K 浏览。
精准测试:文件级影响反查
记录每个测试实际经过的代码,源码变更后反查会影响哪些测试。可以做文件级,也可以做函数级——他选文件级。函数级能少跑一些测试,但采集、分析和维护依赖的成本也不低;文件级会多选一些测试,但实现简单,整体省的时间更多。配置、依赖、初始化这类变更要单独处理(这类变更影响面是全局的)。Vitest 自带的「比较糙,效果有限」。
测试分级:核心路径高频跑
按 BDD 场景描述分清哪些是核心业务路径。高频 CI 只跑三样:核心路径 + 新增测试 + 修改过的测试。其余测试有代码变更就每小时集中跑一轮。代价是部分非核心问题会晚一点被发现——能接受多长时间,看自己的业务。这是明确的工程取舍,不是免费午餐。
Runner 调度:按负载分配机器
容易被忽略的一环。不同测试集对 CPU、内存、IO 的需求不一样,要看机器实际负载分配合适的机器、控制并发度——并发开大了,不一定更快。他们也用了本地 runner(自己配置好的机器),在他的场景里成本比 GitHub 托管 runner 低不少。
作者附赠的两篇论文(放评论区)
Ekstazi(ISSTA 2015 ACM SIGSOFT 杰出论文):论证为什么文件级选择虽然可能多跑一些测试,整体反而更划算。utexas.edu 论文页
Google TestSage:大规模 Web 服务如何结合运行时记录和静态分析,选择需要重跑的测试。UIUC Lingming Lab
好了,测试相关的分享先告一段落。接下来会逐步聊聊,为什么只靠测试还不足以保证软件质量,以及 AI 写的代码该怎么 review、重点看什么。
工程解读:这套方法论在测试版图里的位置
他的「E2E 优先 + 禁 mock」在教科书眼里是异端——但它恰好是对 vibe coding 时代的正确响应。理解了时代背景,争议就消失了。
| 流派 | 主张 | 代表 | 适用前提 |
|---|---|---|---|
| 测试金字塔 | 70% 单元 / 20% 集成 / 10% E2E,追求反馈快、定位准 | Google《Software Engineering at Google》 | 人写代码、变更节奏可控、单元边界稳定 |
| 测试奖杯 | 集成为主(Integration-heavy),单元只留给纯逻辑,E2E 少量冒烟 | Kent C. Dodds(Testing Trophy) | Web 应用、重构频繁、追求抗重构 |
| 本系列立场 | E2E 优先 + 禁 mock + 精准测试补速度 | @yaogangqiang 实践 | AI 高频重写代码、行为契约比实现结构稳定得多 |
关键洞察:三个流派没有对错,只有前提不同。AI 时代最大的变化是实现结构的稳定性崩塌了——AI 一天能把你内部模块重构八遍,但业务行为(下单不重复扣款)几乎不变。测实现的测试跟着重构碎一地,测行为的测试纹丝不动。这就是「测试行为不测试实现」从老生常谈变成生存法则的原因。他用精准测试 + 分级补回了 E2E 慢的短板——Google 用金字塔避免的问题,他用「选着跑 + 分批跑」解决。
精准测试 DIY:Vitest 自带 vs 自建
他说 Vitest 自带的精准测试「比较糙,效果有限」。糙在哪,自建要补什么——这是 TypeScript 项目可以直接动手的部分。
Vitest 自带(vitest related / coverage)
- vitest related [files]:基于模块依赖图(import 关系)反查关联测试
- 只看静态依赖,看不到运行时真实执行路径——动态 dispatch、回调触发的代码覆盖不到
- 依赖图是全量的,不知道「这次变更真的会影响那个测试吗」,宁可多跑也不会少跑
- 胜在零成本,配 --changed 即可用,适合起步
自建文件级选择(他要的效果)
- 跑测试时用 V8 coverage(--coverage)记录每个测试真实经过的文件集合,落盘成 map:testFile → [sourceFiles]
- 源码变更后反查 map:改了 A.ts → 找出所有经过 A.ts 的测试,只跑它们
- 维护成本:map 会过期,需要定期全量跑刷新;动态 import、monorepo 边界要特殊处理
- 这正是 Ekstazi 的思路(论文里叫 class/file-level test selection),工程上完全可自建,几百行脚本起步
选型决策:什么团队该抄,什么团队别抄
他的数字(50 次部署/天)说明这不是玩具团队。抄之前先过一遍这个决策表。
直接抄
AI 写码占比高 + SaaS/To C 业务 + TypeScript 栈 + 部署频繁。六军规全部适用,精准测试从 vitest related 起步,量大再自建 coverage map。
抄一半
有强合规/支付/医疗场景:军规 1/2/5 照抄,但「禁止 mock」要放宽——监管环境里外部依赖本来就要挡;「每小时批量跑」的延迟容忍也可能过不了审计。
先别抄
部署频率一周一次以内、CI 从没排过队:精准测试和分级是给「跑不动」的团队准备的,你的瓶颈不在这,先把军规 1/2 写进 AI 的系统提示词更划算。
他没说的三件事(压力测试)
① Flaky 治理缺席。E2E 优先的团队,头号杀手是 flaky test(偶发失败)。他一个字没提——而这一课的学费往往比精准测试还贵(重试策略、隔离、检疫区)。② 团队规模未披露。一天 50 次部署、300 轮测试背后,大概率有专人维护测试基建。一人团队照抄「自建精准测试」,可能维护 map 的时间比省下的 CI 时间还多。③ 调教成本被隐藏。「教会 AI 写对测试」的规则维护(prompt、lint 规则、review 惯例)是持续投入,不是一次性配置。
苏格拉底对话:让 AI 写测试,为什么总是碎
一个 vibe coding 重度用户和这个系列的碰撞。
学生:我让 AI 给我的模块写测试,覆盖率一下到 95%,看着挺美的。但每次小重构,测试碎一片,我现在花在修测试上的时间比写功能还多。是我的用法有问题吗?
问一下:碎掉的那些测试,断言的是什么?
学生:大多是「这个函数被调用了两次」「返回的数组长度是 3」这种。
那你已经踩中第一条军规了:测试行为,不测试实现。「函数被调用两次」是实现细节,「重复下单不会重复扣款」才是行为。判据就一条——业务没变,重构后测试碎了,那多半是写错了,不是重构错了。
学生:可这些测试是 AI 写的,它照着我的实现生成的,能不贴着实现走吗?
这就是最深的坑:AI 写完代码再照着自己的实现写测试,是用实现证明实现,循环论证。破法是 BDD——先写验收场景(Given/When/Then,user 开头),让场景先行,代码后行。测试和实现必须来自不同的源头,互相才有约束力。
学生:明白了,那我让 AI 先写场景再写代码。但它写测试还是喜欢一通 mock,mock 了个假的 Redis,测试全绿,上线照样炸。
mock 的 Redis 测的是「你的代码以为 Redis 是什么样」,不是「Redis 真的是什么样」。所以第三条军规:能用真的就用真的,不好接就注入 fake——但 fake 要校验和真实系统的契约。AI 喜欢随手 mock,因为 mock 让测试最容易变绿,而变绿是它被奖励的方向。
学生:那我把依赖全换成真的,测试又慢得没法忍,CI 一跑四十分钟,改一行等半天。
到第三幕了:速度问题不是靠少测解决,是靠选着跑解决。记录每个测试真实经过的文件,改了哪个文件就只跑经过它的测试;核心路径每次跑,其余每小时批量跑;再看 runner 的负载分配。他 300 轮/天就是这么活的。
学生:可这些基建,我一个人做项目,值得吗?
好问题,也是这个系列没回答的。一个人项目的答案可能是:军规全要(它们是写给 AI 的提示词),基建缓建(vitest related 一行配置先跑着)。等你 CI 开始排队,再回来抄第三幕。工具永远跟着痛走,不跟着推文走。
个性化洞察:这套东西怎么落到你手上
基于你的背景定制:QA 出身、TypeScript/Node 栈、独立开发多个 AI 产品、日常重度 vibe coding。
六军规直接变成你的 AI 系统提示词
你为什么相关:你的项目全是 AI 写码,军规针对的就是 AI 的三个惯性病(测实现、随手 mock、凑覆盖率)。怎么做:把「测试行为不测实现 / 禁 mock 用真实依赖或契约 fake / BDD Given-When-Then 先行」三条写进各项目的 CLAUDE.md 测试规范段,一次配置长期生效——这本身就是军规 6 的 lint 化思路。
vitest related 是你今天就能白嫖的精准测试
你为什么相关:TS/Node 栈,symphony、happyread 这些项目改一行全量跑 CI 的痛你会越来越频繁地遇到。怎么做:CI 里把 `vitest related $(git diff --name-only HEAD~1)` 接进去,零基建拿到文件级选择 80% 的收益;等测试集上千再考虑自建 coverage map。
BDD 场景是你一人团队的「PM 文档」
你为什么相关:独立开发没有 PM 帮你澄清需求,需求澄清恰恰是 vibe coding 时代最大的质量短板(第一幕的结论)。怎么做:新功能开工前先让 AI 只产出 Given/When/Then 场景清单,你审场景而不是审代码——场景就是需求文档,还能直接转测试。这一步把「需求澄清」和「测试设计」合并成了一次劳动。
盯住作者的下一条:AI 代码 review
你为什么相关:系列终局预告「为什么只靠测试不足 + AI 代码该怎么 review」,和你 QA 背景 + AI 工程的双重视角完全同频,大概率还会带出 Agent/harness 工程实践(他的主业)。怎么做:关注 @yaogangqiang,或等下一条出来再让我跑一次同样的解读流水线。
精选评论
主推文 11 条回复中的高价值增量。
@yaogangqiang(作者补充,5h):两篇精准测试论文的出处——Ekstazi 获 ISSTA 2015 ACM SIGSOFT 杰出论文奖,解释「为什么文件级选择虽然可能多跑一些测试,整体反而更划算」;Google TestSage 讲大规模 Web 服务如何结合运行时记录和静态分析选择需要重跑的测试。把「个人经验」锚到了学术文献上,这是整条推文可信度的关键一跃。
@YMaxwellHayes:「一天 300 轮测试的量级,我们内部的经验是三件事最见效:按变更文件反查影响的测试子集只跑相关的、把外部依赖全部替换成本地假实现、慢测试单独分层每天跑一次。Vibe coding 之后代码量涨得快,测试的分层比测试的数量更重要。」——独立信源交叉验证了作者的方法论,且最后一句是全系列最好的注脚。