3步用AI给祖传项目补上单元测试

老项目不敢改,根源不是代码烂,是没有测试兜底。让 AI 补测试听起来很美,但直接丢一句「给这个文件写单测」,你大概率会拿到一堆覆盖率 90%、却连一个真实 bug 都抓不住的摆设。下面这三步,是能落地的做法。

第一步:让 AI 排优先级,而不是全量开测

一个五万行的项目,全量补测试是三个月起步的工程。正确做法是先测「改动最频繁 × 逻辑最复杂」的那 10 个文件。

把项目目录树和最近半年的 git 提交统计一起喂给 AI,提示词可以这样写:

这是我们仓库的目录结构和最近 180 天的 git log 统计(每个文件的修改次数、参与人数)。请按「修改频率 × 圈复杂度 × 历史故障密度」排出最该补测试的 10 个模块,说明每个模块的核心不变量是什么。

「核心不变量」这个要求很关键。AI 会告诉你,订单模块真正不能破的是「实付金额 = 商品总价 – 优惠 – 运费」,而不是某个 getter 返回值。测试要保护的是不变量,不是代码行。

第二步:一次只喂一个函数,先要边界清单再要代码

新手最常见的错误是把整个 800 行文件扔给 AI,然后得到 800 行同样臃肿的测试。改成一次一个函数,并且分两轮对话:

第一轮只要清单:「这个 calculateDiscount(user, cart, coupon) 函数有哪些边界条件?请列成表格,包含输入组合、预期行为和触发原因。」

你会拿到一份人类容易漏的列表:优惠券过期但用户是会员、购物车为空时的除零、浮点金额的舍入精度、并发下同一张券被用两次。

第二轮才让它写测试,并且必须附上四样东西:函数源码、它调用的外部依赖、项目现有测试的写法样例、mock 策略(哪些该 mock、哪些用真实实现)。不给现有测试样例,AI 会自创一套风格,和你的 pytest fixture 体系打架。

还要显式禁用一类测试:任何断言里出现被测函数自身的调用,比如 expect(discount(...)).toBe(discount(...))。这是同义反复,永远为真,AI 在上下文不足时特别喜欢这么写。

第三步:用变异测试验货,别信「全部通过」

AI 写完测试,npm test 全绿,这不代表什么。真正的问题是:把源码里的 >= 改成 >,你的测试会红吗?如果不会,这条测试就是装饰品。

这就是变异测试的用途。JS 项目用 Stryker,Python 项目用 mutmut,它会自动篡改源码里的运算符、返回值、边界值,然后跑你的测试套件。存活下来的变异体,就是测试的盲区。

实操节奏建议:先只对刚补完测试的那几个文件跑变异测试,把「存活变异体」列表丢回给 AI,让它针对每一个存活变异体补一条能杀掉它的测试。这个闭环跑上两三轮,测试质量会有质变。

一个真实经验:AI 生成的测试首次变异得分通常在 40%~60%,经过两轮定向补测可以拉到 80% 以上。而纯靠覆盖率导向生成的测试,变异得分经常低于 30%。

收尾:把流程固化,而不是一次性工程

补完这一轮测试只是起点。更重要的两条纪律:

一是把新生成的测试接入 CI,并对新增代码强制覆盖率门槛,存量代码设一个老门槛只准升不准降。二是把上面三步写成一份团队提示词模板,放进仓库的 CONTRIBUTING.md,让每个改 bug 的人都顺手把复现用例沉淀成回归测试。

AI 在这里的角色不是「帮你写代码」,而是「帮你把隐藏在脑子里的边界条件,逼成可执行的断言」。想清楚这一点,补测试这件事才真正省力。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...