给一个没有测试的工具函数,让 AI “补齐单元测试”,它通常能很快交出十几条用例,覆盖率也会变好看。问题是数量不等于保护力。有些用例只是在重复实现,有些断言永远不会失败,还有些为了适应错误行为,悄悄把本来应该暴露的 bug 固化成了预期。
AI 最擅长扩展边界清单
我会先写两三条最重要的行为:正常输入得到什么、非法输入如何处理、哪条业务规则不能退化。然后让 AI 阅读实现和已有测试,补充“还缺哪些边界”,先只列清单不写代码。它经常能想到空数组、Unicode、时区边界、重复提交和并发顺序这些容易遗漏的情况。
清单确认后,再选择有意义的项目生成测试。这样人负责测试意图,AI 负责机械展开,分工比较合适。
先不要写测试代码。
根据函数契约列出边界场景,并说明每个场景
可能发现哪类缺陷。不要根据当前实现推测预期。
三种看起来很忙、实际很弱的测试
第一种是把内部实现全部 mock 掉,最后只验证 mock 被调用。它和真实行为相距太远,重构一下就碎。第二种是大快照,几百行输出变化时没人认真看 diff。第三种是没有关键断言,只检查函数“没有抛错”。
AI 还可能读取实现细节后,把现有 bug 当成规范。所以提示里要单独提供函数契约或需求,而不是让它仅凭代码猜。发现测试和需求冲突时,应先停下来确认,不能直接修改断言让绿色回来。
测试通过只能说明实现符合测试;测试是否符合需求,是另一层判断。
生成之后,我会故意让它失败一次
我的流程是:先确定契约;让 AI 提边界清单;挑选用例;生成小批测试;审查断言;最后临时改坏实现,确认关键测试真的会红。这个“故意破坏”很有效,它能立刻发现那些无论实现怎样都通过的空心测试。
评审生成测试时,我也会问三个问题:失败信息能不能看懂?测试是否只关心外部行为?未来合理重构会不会被无关细节阻挡?答不上来就删掉或重写,没必要因为是自动生成就全部保留。
AI 确实让补测试变快了,尤其是重复数据和边界组合。但它最大的价值不是帮我把数字推到 90%,而是像一个不嫌麻烦的搭档,陪我把“还有哪些情况”多问几遍。