覆盖率从 61% 提到 84%,团队松了口气;两周后线上还是崩在一个空数组上,而那个函数明明「有测试」。用 AI 补单元测试能把这件事加快,但补哪里、边界怎么想,仍然要先定下来——写测试的人习惯给正常输入配一个用例,真正的故障几乎都发生在正常之外。
覆盖率涨了,故障没少
覆盖率衡量的是执行过的代码行占比,它不区分断言写得对不对。一个只调用函数、不断言结果的测试,也能把覆盖率刷上去。更隐蔽的是断言写反:测试通过,是因为它断言的本来就是错误行为。补测试之前先接受一件事:这个数字只能用来找「完全没测过」的区域,不能用来判断测得对不对。
先补哪一类,顺序有讲究
四类优先:出过问题的地方,每修一个缺陷就补一个能复现它的用例,这类用例已经被现实验证过,价值最高;分支多的地方;改动频繁的地方;核心链路上的关键计算。按这个顺序补,同样花一天,拦下的故障数量差别很大。
反过来做也常见:从工具类、取数函数、纯格式化函数开始,因为这些写起来最省事,模型也最擅长。写完之后覆盖率很好看,风险最高的那段业务逻辑仍然一行没测。
五步走完一轮补测
从挑函数到进回归,五步各有产出物,也各有各的坑:
| 步骤 | 产出物 | 常见坑 |
|---|---|---|
| 选目标 | 按「出过缺陷、分支多、改动频繁、核心计算」排序的清单 | 平均用力,给一个取数函数写了八个用例 |
| 划输入域 | 每个参数的合法区间、边界值、非法值 | 只写正常值,边界全靠临时灵感 |
| 生成用例 | 用例表:输入、预期结果、判断依据 | 断言写成「不抛异常就算过」 |
| 验证会红 | 故意改坏实现后失败的用例清单 | 测试全绿但从不失败,等于没测 |
| 收进回归 | 命名带缺陷编号、可独立运行、慢的隔离 | 用例互相依赖,单独跑一个就挂 |
第四步最容易被跳过。测试跑绿不等于测试有效,把实现故意改坏一处,对应的用例必须变红;不变红,说明它测的是别的东西,或者根本没断言到点子上。
边界按六个方向过一遍,别靠灵感
靠灵感想边界,想到第三个就断了。按固定清单过,漏得少:空与缺失(空字符串、空数组、字段没传)、极值(0、负数、最大值、超长输入)、类型不符(该传数字传了字符串、传了空值)、顺序与重复(重复调用、乱序到达、重放)、并发(同时改同一条数据)、依赖失败(下游超时、返回错误码、返回半截数据)。
再补三类业务代码里高频出现的:时间(跨天、跨时区、闰年)、精度(浮点比较要用容差,金额别用浮点)、编码与长度(中文、表情符号、超长文本被截断)。这些不是理论清单,是线上事故的常见来源。
把代码交给它之前,先交代约束
直接贴一个函数说「帮我写测试」,得到的多半是几个正常路径的用例,外加一两个想当然的边界。有效的输入包含四样:函数的用途、输入从哪来、必须成立的不变量、已有的测试风格。再补两条禁令:不要改生产代码,不要引入新的测试依赖。
它会编造不存在的方法和字段,尤其是当你只贴了片段、它看不到完整定义的时候,这类虚构在代码里表现为调用一个不存在的接口,跑一次就报错,反而好发现。更难发现的是它给了一个能跑通、却断言了错误行为的用例。把约束写进提示词,比事后逐个改要省事。
测试写歪了,比没写更贵
测试也是代码,也要维护。三类写法会让维护成本失控:断言内部实现(调用了哪个方法、调用几次),一重构就全红;用例之间共享状态,单独跑就挂;为了凑覆盖率给每个分支写一个几乎一样的用例,业务一改要修十几处。
可用的原则是只测行为:给什么输入、得到什么输出、抛出什么错误。这样重构内部结构时,测试不用动。慢的用例(连数据库、起服务)单独隔开,别让整个套件跑一次要十分钟——跑不动的测试,最后没人跑。评估指标怎么选、怎么读这件事,在覆盖率上同样适用:指标要能指导动作,不能只用来汇报。
它想不出的那几类用例
业务语义它猜不到。「这个字段在退款场景下应该保持原值」这种规则,只存在于需求文档和老员工的记忆里,代码里看不出来,模型也推不出来。领域内的边界值同理:账期按自然日还是工作日、金额保留几位、状态机有哪些合法迁移,这些要靠人给。
真实流量的形态它也不知道。线上请求的长尾分布、某个字段实际出现的脏数据长什么样,只能从日志和监控里捞。把这批真实样本脱敏后交给它归纳,比让它凭空想边界有效得多。用一部分样本验证、留一部分检验的思路,在测试样本的挑选上可以借用。
最后是取舍:哪些边界值得测、哪些属于已知限制不必测,取决于故障的后果,模型没有你的风险偏好。
速查:补测试时的五条
- 先补出过缺陷的地方,再补分支多的
- 每个参数都过一遍空值、极值、类型错
- 新用例先确认它会红,再收进套件
- 只断言行为,不断言内部调用
- 慢用例隔离,别拖垮整轮


