团队今年定了个硬指标:核心模块单测覆盖率从百分之三十五提到七十。指标一下来,组里哀鸿遍野------不是不会写单测,是没时间写。存量代码六万行,按过去的手速,光补测试就要两个人干一个季度。
四月我们搭了一条 AI 辅助生成单测的流水线:解析接口定义和现有代码结构,自动生成测试用例骨架和断言初稿,人工只做审查和补充边界。五周跑下来,覆盖率从三十五爬到六十八,人工投入不到原计划的三成。这篇讲流水线的设计,也讲哪些环节 AI 干得好、哪些环节它干不好必须人来。
一、先把期望摆正
1.1 AI 生成的单测能信几分
先说结论:AI 生成的用例骨架可用率八成,断言初稿可用率六成,完整可提交的直接可用率不到一半。听起来不高?换个算法:过去写一个接口的完整单测平均四十分钟,现在骨架加初稿两分钟出来,人工审查修改十分钟,单接口十二分钟。效率是三倍出头,不是十倍------把它当三倍效率的工具,体验很好;当十倍效率的魔法,必然失望。
1.2 别让它测业务语义
试错后划的边界:结构性测试 AI 包办,业务语义测试人来。什么意思------参数校验、边界值、空值、异常抛出、幂等性,这些从接口签名就能推导的用例,AI 生成质量很高;而"金额计算要按合同条款四舍五入到分"这种语义规则,AI 看代码看不出来,它只能把代码现有的行为固化成断言------如果代码本身写错了,AI 会忠诚地为错误行为生成通过的测试,把 bug 用测试保护起来。这是 AI 生成单测最大的暗坑。
二、流水线设计
2.1 三段式结构
流水线分三段。解析段:读取接口定义、入参出参类型、既有代码,产出结构化的接口描述。生成段:把接口描述喂给模型,按模板批量生成测试用例。校验段:生成的代码先过编译和静态检查,再实际跑一遍,跑不过的自动丢弃或标记。三段之间用中间产物解耦:接口描述和生成结果都落盘成文件,任何一段重跑不影响其他段------这个设计后来数了我们:生成段换模型重试三轮,解析和校验纹丝不动,排查问题也只看自己那段。核心脚本不到两百行:
python
def gen_tests_for(module_path):
apis = parse_interfaces(module_path) # 解析接口签名与类型
suites = []
for api in apis:
prompt = build_prompt(api, template=SINGLE_API_TPL)
cases = call_llm(prompt) # 生成用例骨架+断言初稿
for c in parse_cases(cases):
if compile_ok(c) and static_check(c):
verdict = run_case(c) # 实际执行
c.status = "pass" if verdict.ok else "review"
suites.append(c)
return suites
校验段里 run_case 这步最值钱:生成的用例实际执行一遍,能跑且断言通过的标记 pass,直接进候选;跑不过的标记 review,人工优先看。用执行结果给生成质量分诊,人只审机器筛过的少数派,效率差就拉开在这里。
2.2 提示词模板的三个关键
模板里最重要的三块。其一,喂上下文:不only 接口签名,连实现代码、关联的领域类、已有测试的风格样例一起给,生成的代码才和项目现有风格一致,review 成本才低。其二,要求分类型生成:模板里列出必测类型------正常值、边界值、空值、非法类型、重复调用,让模型逐类输出,防止它一股脑全写正常路径。其三,禁止造数据:要求测试数据从测试夹具工厂取,不私自硬编码,否则生成的用例在持续集成环境里跑不过。
2.3 存量代码的分批策略
六万行存量不可能一口气生成。按模块风险分三批:线上故障频发的高风险模块先补,公共工具类次之,边缘功能最后。每批生成完,先跑全量回归,确认生成的测试没有引入误报,再合入。第三批做到一半发现时间富余,把生成标准从行覆盖提升到分支覆盖,最终覆盖率比原计划还多了三个点。
分批还有个管理上的好处:每批结束都是一次可展示的里程碑,开头那批高风险模块补完时覆盖率已经到五十几,领导看到中间成果就放心了,不会等到五周末才见到效果。技术项目向管理层的汇报节奏感,跟技术本身一样重要。
三、人工审查怎么审
3.1 审查清单四条
人工审查按固定清单走,四条:断言语义是否正确------AI 有时把期望值抄成了实际值的镜像,断言恒真;边界是否覆盖------AI 常漏的边界是空集合和单元素集合;测试数据是否合理------有没有用真实环境才能造出的数据;命名是否达意。清单四条,平均每个用例审查时间四十秒,效率的最后一公里靠的是流程纪律而不是技术。
3.2 断言恒真的坑
重点讲讲断言恒真,这是 AI 生成测试的头号质量问题。典型症状:断言写成了 result.equals(result),或者期望值引用了被测函数自己的输出。表面看测试全绿,实际什么都没验证。我们在校验段加了变异检测抽检:随机改动被测代码的一处返回值,重跑测试,全绿不变的就标红------连代码改错了测试都不红,这测试等于白写。变异抽检让一批恒真断言现了原形,上线头两周就抓出三十多个。
顺带说一句审查体验层面的事:审查人看到"全绿"的用例,天然觉得它靠谱,扫一眼就过。所以我们的审查界面把待审状态的用例排在最前面,全绿的压在后面------先看可疑的,再看顺的,注意力分配要逆着人性设计。这个小改动让审查质量又提了一截。
四、五周实账
4.1 数字
五周成果:覆盖率三十五到六十八;生成用例四千二百个,人工审查通过两千七百个,通过率六成四;人工投入折合十四人天,对比原计划五十人天。生成的测试至今在持续集成里跑了七周,误报率百分之一点二,和手写测试相当。
4.2 意外收获
流水线跑起来后有个意外收获:AI 生成测试时暴露了一批存量问题。有十几个接口,AI 按类型推导生成的合法输入,实际一跑就抛未捕获异常------空字符串、超大整数、并发重复调用。这些接口带了伤跑了几年,只是从来没人这么喂过它们。测试流水线顺手做了一次义务模糊测试,这批问题单独立了缺陷单修复。
另一个收获在团队认知层面。以前大家觉得补单测是苦役,现在看它变成了审查活:泡杯茶逐条看断言,看得快的半天审三四百条。有个同事说得妙:过去写测试是体力活,现在像阅卷。体力活和阅卷活的疲劳曲线完全不同,阅卷活甚至可以放在下午精力不济的时段干------工作安排的弹性反而大了。
常见问题
问:AI 生成的测试维护成本会不会更高?
答:我们的经验是略低。生成测试严格遵守模板和夹具工厂,风格统一,比十个工程师十种写法的手写测试更好维护。要担心的反而是模板本身:模板改了,存量生成测试要跟着刷一遍,好在生成是流水线,重刷是机器的活,一条命令的事。还有一个隐性收益:模板本身就是团队测试规范的机器可执行版,新人的测试风格不用得带,生成一遍就归正了。
问:什么语言什么框架适用?
答:强类型语言效果显著更好------类型信息越丰富,AI 能推导的边界越多。我们 Java 项目的生成质量明显高于动态语言项目,动态语言那边类型靠精,模型只能靠精实现代码和命名猜边界,断言可用率掉了一截。框架不是障碍,提示词模板里给两三个目标框架的示例,模型切换得很熟练。真正受限的是测试环境:依赖真实数据库或外部服务的接口,生成后还需要人做替身改造,这部分占去了审查时间的三成。
问:会不会让新人丧失写测试的能力?
答:我的看法是不会,但前提是审查环节不走过场。审查本身就是在读测试、改测试,新人审得多了,套路自然内化------事实上组里新人现在写的测试越来越像模板风格,说明模板里沉淀的最佳实践被吸收了。能力退化只会发生在"生成后直接合入"的团队,那不是 AI 的问题,是流程的问题。
问:投入产出怎么跟领导算账?
答:一个数:十四人天换五十人天的存量补测,外加覆盖率翻倍和十几个存量缺陷浮出水面。这套流水线本身就是三个人一周的业余时间搭的,没有采购,没有新硬件。要复述给领导听的话------我们没有增加测试人力,我们把测试人力放到了只有人能做的语义校验上,机器干机器的,人干人的。之后的增量代码也一样:新接口合入前跑一遍流水线,测试债不再新增,这才是不用年年补课的根本。