为什么 AI 生成单元测试总是写出垃圾代码?

技术宅小李 初级 2小时前 392 浏览 9 点赞 约 3 分钟

上周四下午 3 点,我被一个极其离谱的 Bug 搞崩了。

为什么 AI 生成单元测试总是写出垃圾代码?

当时我在给一个复杂的订单状态流转逻辑写测试用例。为了省事,我直接把一段 300 行的 Java 业务代码丢给 AI,要求它帮我生成全覆盖的单元测试。结果 AI 吐出来的代码看着极其专业:Mock 了所有依赖,断言(Assert)写得密密麻麻,甚至还帮我处理了边界值。

我直接 Ctrl+C 粘贴到 IDE 里,运行。

全绿。

我当时还挺得意,觉得效率起飞了。直到第二天,代码上线后,生产环境突然爆出一个 NPE(空指针异常),而且正好是在那个 AI 帮我写了“全覆盖”测试的逻辑分支里。

我去翻那个测试用例,才发现 AI 犯了一个极其低级但隐蔽的错误:它在 Mock 对象的返回值时,为了让测试通过,直接手动写死了返回结果,而这个结果正好规避了真实业务中可能出现的 null 值。

简单说,AI 帮我写了一套“为了通过而通过”的假测试。

这种“假覆盖率”比没有测试更可怕

很多新手用 AI 生成单元测试的时候,最容易掉进这个坑:只看覆盖率报告,不看测试逻辑。

我对比了一下 AI 生成的测试代码和手动写的代码,区别简直离谱。看这张表:

| 维度 | AI 盲目生成的测试 (我的踩坑版本) | 经过复盘修正后的测试 |
| :--- | :--- | :--- |
| Mock 策略 | 只要能让代码跑通,随便 Mock 返回值 | 严格模拟数据库/外部接口的真实边界行为 |
| 断言重点 | 验证方法是否被调用了 1 次 (verify) | 验证输出结果在极端输入下的正确性 |
| 覆盖率 | 表面 100% (行覆盖) | 逻辑 100% (场景覆盖) |
| 维护成本 | 业务改一行,测试崩十行 | 语义化清晰,随业务自然演进 |

当时我最抓狂的是,我尝试调整 Prompt,告诉它“请考虑 null 值情况”,结果它给了我 5 个几乎一模一样的测试方法,只是把变量名从 testCase1 改成了 testCase2

AI生成单元测试

在 PromptCube 社区里翻到的那个“救命”技巧

这种死循环让我意识到,单靠调整提示词可能没用,得看别人是怎么在实际工程里处理的。

我在 PromptCube 社区逛的时候,看到一个资深开发者分享的实战帖。他提到一个观点:不要让 AI 直接写整个测试类,要让它写“测试矩阵”

这个思路对我启发很大。我试着改变了方案:
1. 我不再要求 AI “写单元测试”,而是要求它“分析这段代码所有可能的逻辑分支和失效点,列成表格”。
2. 拿到表格后,我挑选出最容易出 Bug 的 3 个场景,手动写 Mock 逻辑。
3. 最后,我把手动写好的第一个用例作为 Few-Shot(少样本)喂给 AI,让它模仿这个逻辑去补全剩下的 5 个场景。

这种方法虽然慢了一点,但实测有效。我重新跑了那个订单逻辑,瞬间抓到了 2 个隐藏的空指针风险。

在社区里,大家讨论得最激烈的其实不是哪个模型更强,而是在 AI模型讨论 板块里,大家会具体拆解不同模型对特定语言(比如 Kotlin vs Java)生成测试代码的逻辑差异。有人发现某些模型在处理异步测试(Async Test)时极其不稳定,经常在 CountDownLatch 的等待时间上乱写。

具体的报错与解法记录

这次踩坑最具体的报错其实是这个:java.lang.NullPointerException,发生在 OrderService.calculateTotal()

AI 生成的错误解法:

// AI 以为这样就覆盖了
when(orderRepo.findById(any())).thenReturn(Optional.of(mockOrder)); 
// 它永远给一个对象,导致测试永远通过

我修正后的真实解法:

// 必须强制模拟不存在的情况
when(orderRepo.findById(any())).thenReturn(Optional.empty()); 
// 此时测试报错,暴露出业务代码里没写 .orElseThrow(),Bug 现形

这个坑让我意识到,AI 生成单元测试的本质不是“替代写代码”,而是“辅助找场景”。如果你把它当成自动生成工具,你最终得到的是一堆毫无意义的绿色勾号,以及在生产环境下随时会爆炸的定时炸弹。

现在我参与 PromptCube 社区的流程基本变成了:先在社区搜类似的业务场景 → 看看大佬们怎么定义测试边界 → 用 AI 快速填充重复代码 → 手动审核断言。

这种链路比盲目相信 AI 要稳得多。

全部回复 (0)

还没有回复,来发第一条吧!

发表回复

支持 Markdown 格式