AI Agent 自动生成单元测试怎么做:从断言质量、边界用例到人工复核的完整清单

AI Agent 自动生成单元测试怎么做:断言质量、边界用例与人工复核清单

没有哪个开发喜欢写单元测试。所以当「让 Agent 自动生成测试」这件事出现时,大家的期待值都很高:代码写完,一句指令,测试就有了,覆盖率还很好看。

真跑起来你会发现一个挺尴尬的现象:测试全绿,线上照样出问题。为什么?因为 Agent 生成的很多测试,看起来像测试,其实什么都没验证——断言写的是「结果不为空」,用例全绕着最顺畅的那条路径走,外部依赖被 mock 得干干净净,被测的那段逻辑压根没真正被执行到。

这篇不讨论「该不该让 Agent 写测试」(该,重复劳动就该交出去),只讨论一件事:怎么让 Agent 写出来的测试真的能拦住 bug。

一、先认清:Agent 写的测试为什么常常是「假的」

三类问题,几乎每次都会出现:

  • 空断言。最常见。断言只写「不报错」「结果不是 None」「长度大于 0」。这类断言在任何实现下都能通过,包括错误的实现。
  • 只测顺路径。Agent 天然倾向于生成「输入正常、流程走完」的用例,因为这类用例最好写、最容易过。异常分支、边界值、并发冲突几乎不会主动覆盖。
  • mock 掉一切。为了让测试跑得快、不依赖外部环境,Agent 会把所有外部调用都打成桩。结果是测了半天,测的是 mock 的行为,不是被测代码的逻辑。

根子上说,这三类问题有一个共同原因:Agent 的目标函数是「让测试通过」,而不是「让测试能发现缺陷」。不把后者变成显式要求,它就会默认选前者——这是生成测试时最需要处理的一点。

二、生成之前,先把三样东西给足

让 Agent 写好测试,前提是它得知道「这个函数在什么约定下运行」。生成前建议把三样东西一起给它:

  1. 被测代码及其上下文。不是只给一个函数,而是给它完整文件,再加上调用这个函数的那些地方。知道调用方怎么用,才知道哪些行为算「契约」。
  2. 项目已有的测试风格。至少给两三个现成的测试文件当范例:用什么框架、怎么造数据、怎么组织用例分组、mock 用什么库。照着范例写,评审时返工最少。
  3. 明确的验收要求。把要求写进提示里,比如:每个函数的正常路径至少 1 例、异常路径至少 2 例、边界值至少 3 例;禁止出现只判断非空或长度的断言;外部依赖允许 mock,但断言必须落在返回值或状态变化上。

如果你已经把代码库问答那套检索做起来了(代码库问答怎么做),第 2 条其实可以自动完成:把「同一目录下已有的测试文件」作为检索条件召回,效果比手工贴范例稳定得多。

三、断言要验行为,不要验实现

这是整篇最核心的一条。判断一个测试好不好,标准很简单:把被测代码换一种写法但保持行为不变,测试应该照样通过;把行为改坏,测试必须失败。

照这个标准看,很多 Agent 生成的断言是不合格的:

  • 验调用次数(断言某个方法被调用了几次)——实现一变就红,行为没变也红。
  • 验内部变量——耦合实现细节,重构成本直接翻倍。
  • 只验返回值的一部分(只验状态码不验内容)——漏掉的往往正是会出问题的那部分。

写提示的时候可以直接给约束:断言对象限定为「函数返回值」「抛出的异常类型」「可观察的状态变化」这三类,不允许断言内部调用细节。这一条能让测试的有效性上一个台阶。

四、边界条件,是最该让 Agent 补的

人写测试时最容易漏的是边界,Agent 也一样——但这件事恰好是机械的、可以清单化的。把下面这张清单一并交给它,让它逐条对照生成:

  • 空与零:空字符串、空数组、空对象、0、false、None。
  • 极值:超长字符串、超大数字、超出范围的索引、溢出边界。
  • 重复与顺序:重复元素、顺序颠倒、已存在的记录再插一次。
  • 异常输入:类型不对、格式不对、缺必填字段、编码异常。
  • 依赖失败:外部接口超时、返回错误码、返回空、返回脏数据。
  • 首尾与并发:第一条、最后一条、并发同时改同一条记录。

这六类里,依赖失败和并发最有价值,也最容易被跳过。真实事故多数不是「有人输入了一个奇怪的字符串」,而是「下游挂了之后程序没兜住」。让 Agent 专门为这两类生成用例,收益比再刷一百行覆盖率大得多。

五、覆盖率要用对,别当目标

覆盖率是个好指标,但它衡量的是「有没有执行到」,不是「有没有验证到」。100% 行覆盖的测试集可以完全拦不住 bug——因为断言是空的。

正确用法是把它当线索,而不是当成绩

  • 分支覆盖而不是行覆盖。大量 bug 藏在没被走到的 else 里。
  • 新增代码的覆盖率而不是全仓平均。历史代码的数字好看没用。
  • 把「未覆盖代码块清单」丢回给 Agent 让它针对性补——这比让它盲写一轮有效得多。

另外提醒一句:别把覆盖率做成门禁分数。一旦成了 KPI,团队(和 Agent)就会去写「能提高覆盖率但什么都不验证」的测试。门禁该管的是别的东西,站内 质量门禁怎么设 里讲过哪些检查值得卡、哪些卡了反而有害,可以对照着看。

六、生成完,人工必须过一遍

Agent 生成的测试建议一律当草稿处理,合并前人工过四条:

  1. 断言有没有实质内容?没有的,删除或补写,别留着充数。
  2. 有没有把 bug 一起固化?如果现有代码本身就是错的,Agent 会照着错的实现写测试,把这个错「合法化」——它会把错误行为写成期望值。这是最危险的一类。
  3. 测试之间有没有互相依赖?多个用例共用状态、执行顺序不能换,属于不稳定测试,早晚在 CI 上随机变红。
  4. mock 有没有把被测逻辑也挡掉了?重点看 mock 的边界是不是画在了被测函数的外面。

因为生成量大,逐条看成本高,通常做法是抽检 + 关键模块全检:核心交易路径、权限判断、金额计算的测试全看;边缘工具函数按比例抽。抽检规则和样本怎么定,人工复核抽检怎么做 那套方法可以直接搬过来。

七、落地时的三条经验

  • 先只生成、不阻断。第一周别急着让测试卡 CI。先让 Agent 生成、人工修,跑一段时间看产出质量再决定要不要进流水线。直接上阻断,会因为大量无效测试导致误报,最后大家集体申请豁免,等于白做。
  • 一个模块一个模块推。从测试最薄弱、变更最频繁的地方开始。一块做扎实了,有了可复制的提示模板,再推下一块。
  • 把测试当交付物验收。和代码走同一条评审流程:谁生成的、谁审的、断言依据是什么。涉及行为的变更要能追溯到用例,这套记录方式和 变更后验证清单 是同一种思路。

如果你想让 Agent 顺手把「提测试」这件事也做完(开分支、提交、发起 PR),那属于另一段流程,OpenClaw 接 GitHub 那一套 里讲得比较细。注意 PR 描述里要写清「生成了哪些测试、人工改了什么」,评审的人才知道该看哪里。

优缺点和适合人群

优点:重复劳动显著减少;边界用例这种「人一定会漏」的部分能被清单化补齐;老代码补测试的门槛大幅降低。

缺点:无效测试容易大量堆积,反而增加维护负担;对提示和人工复核的要求不低,不是「一键变好」;如果团队本身不写测试,推起来会因为没人愿意评审而卡住。

适合:已有测试体系(哪怕很薄)、有一定代码规范、变更频繁的团队。反过来,如果项目从来没写过测试、也没有评审习惯,建议先从「让 Agent 只生成边界用例清单给人参考」这一步开始,别一步到位。

总结

让 Agent 写单元测试,成败不在模型,在于你有没有把「能拦住 bug」这个标准明确说出来。四条照做:

  1. 生成前给足上下文:完整被测代码、调用方、项目测试风格、明确验收要求。
  2. 断言只验行为:返回值、异常、状态变化,不许验内部调用细节。
  3. 按清单补边界:空零极值、重复顺序、异常输入、依赖失败、首尾并发。
  4. 生成完人工过一遍:重点防「把 bug 固化成期望值」。

最后一句实在话:测试的价值不在于数量,而在于它失败的时候你能立刻知道「哪里坏了」。Agent 负责把量做出来,把「这条断言到底想证明什么」想清楚,还得是人。真要说哪件事值得先做,我推荐先把断言标准定下来——这一条定好了,后面生成的测试质量会成倍地不一样。至于上线前要不要拿这批测试去跑一轮回归,做法可以参考 版本升级后的回归测试Agent 评测怎么做,两者的检查思路是相通的。

发表评论

您的电子邮箱地址不会被公开,必填项已标注 *