在 AI 编程工具的各种用法中,生成单元测试几乎是最受欢迎的一项:写测试枯燥、耗时,而 AI 几秒钟就能生成一大批。覆盖率数字很快变得好看。但很多团队随后发现,这些测试有不少只是把当前代码的行为“拍照存档”,即使代码有 bug,测试也照样通过。 覆盖率提高了,质量保障能力却没有相应提升。

AI 生成测试的常见问题

1. 以实现为准,而不是以需求为准

AI 读取函数代码后生成测试,自然会把代码当前的行为当成正确答案。如果函数本身有错,测试会把错误也固化下来。

2. 断言空洞

只断言“返回值不为空”“没有抛出异常”,而不检查具体结果是否正确。这样的测试几乎不可能失败。

3. 过度模拟

把所有依赖都替换成模拟对象,测试实际上只在验证“函数按预期调用了模拟对象”,与真实行为脱节,一旦重构就大面积失败。

4. 只覆盖正常路径

边界值、异常输入、并发场景、空集合、超长字符串等容易出错的情况被忽略。

5. 测试本身有错误

生成的测试可能引用不存在的方法、使用错误的参数,或者为了让测试通过而修改了期望值。

让 AI 写出有价值的测试

从需求出发

在让 AI 生成测试时,提供函数的需求说明、业务规则或接口文档,而不仅仅是代码。明确告诉它:“根据以下规则编写测试,不要假设当前实现是正确的。”

先列测试用例,再写代码

让 AI 先列出应该测试的场景清单:正常情况、边界情况、异常情况、业务规则的每个分支。人工审查清单是否完整后,再生成测试代码。这一步的效率很高,也最能体现人的价值。

要求具体断言

在提示词中明确要求每个测试都必须断言具体的返回值或状态变化,禁止只检查非空或不报错。

控制模拟范围

只对外部系统(网络、数据库、时间、随机数)做模拟,内部逻辑尽量使用真实对象。

测试驱动开发的变体

先让 AI 根据需求写测试,确认测试失败,再写实现让测试通过。这种顺序天然避免了“以实现为准”的问题。

如何验证测试质量

覆盖率只能说明代码被执行过,不能说明测试能发现问题。更可靠的方法是变异测试:自动对代码做小的修改(例如把大于号改成大于等于、把加法改成减法),如果测试仍然全部通过,说明测试没能发现这个“人为 bug”。变异测试得分是衡量测试有效性的好指标,虽然运行成本较高,但可以定期对核心模块执行。

另一个简单的方法是人工故意引入几个典型错误,看测试能否捕获。

人工审查的重点

审查 AI 生成的测试时,重点检查:

  • 测试名称是否清楚描述了被测场景;
  • 断言是否验证了业务上真正重要的结果;
  • 是否覆盖了关键边界条件;
  • 是否有为了通过而“迁就”实现的期望值;
  • 测试之间是否互相依赖、执行顺序是否影响结果。

合理的期望

AI 生成测试最适合的场景是:为已有的、行为确定的工具函数补测试;把人列出的测试清单快速转化为代码;为遗留代码建立行为基线,以便安全重构,相关方法见用 AI 理解和重构遗留代码。在这些场景中,它能显著节省时间。

小结

AI 能快速产出测试代码,但测试的价值在于发现问题。从需求而不是实现出发、先审测试清单再生成代码、要求具体断言、用变异测试检验质量,才能让 AI 生成的测试真正成为安全网,而不是一张好看却漏洞百出的覆盖率报表。更多质量相关实践见用 AI 做代码评审。