AI帮你造测试数据?先别高兴

AI Mock数据可靠性

Expedia 开源了一个叫 mockql-rs 的 Rust 工具。功能很直观:GraphQL 查询里标一个 @mock,LLM 就帮你生成模拟响应。Airbnb 四月份也搞了类似的东西,叫 @generateMock。加上 GraphQL 基金会二月发的 RFC,半年内三个方案冒出来,解决同一个问题——手工写 mock 数据太烦了。

听起来很美。schema 给你画好了框,AI 往里面填内容,开发者终于不用维护两百行的 JSON fixture。但仔细看看这三家做的事,问题比它们解决的问题更多。

三家公司,三种理解

Expedia 把 mock 做成了运行时代理:工具作为独立进程挡在客户端和服务器之间,标注了 @mock 的字段由 LLM 现场生成,没标注的字段转发给真实后端。一次查询可以同时拿到真实数据和假数据,客户端分不出来。

Airbnb 走的是构建时路线。@generateMock 在代码生成阶段处理,产出 JSON 文件和类型化的访问函数,给快照测试和演示应用用。而且 Airbnb 有意保留了工程师手动编辑生成结果的口子——生成之后你还能改。

RFC 的立场又不一样。它把 @mock 定义在操作级别而非字段级别,用名称参数选择不同的预设响应。客户端检测到 mock 数据漂移时,必须做纠正。LLM 生成在 RFC 里只是个"推荐策略",不是机制本身。

同一个指令名 @mock,两家含义不同。这不是语法分歧,是设计哲学的根本分歧:Expedia 要的是灵活,Airbnb 要的是可控,RFC 要的是标准化。

非确定性 mock 的根本问题

LLM 每次生成的数据都不一样。这对 demo 无所谓,对测试是致命的。

假设一个查询的 hint 是"附近五家热门餐厅"。LLM 可能生成意大利餐厅,也可能生成日料。今天 CI 跑通了,明天同样查询同样 hint,返回的餐厅名、描述、评分全变了。这不是 flaky test——flaky test 至少有明确的失败原因。这是测试数据本身在漂移,而且漂得毫无征兆。

Airbnb 显然意识到了这个问题,所以把数据固化到构建产物里——生成一次,后续手动维护。RFC 则要求客户端检测漂移并纠正。但两家都没正面回答一个更基础的问题:当 mock 数据不可重复时,"测试通过"到底意味着什么?

确定性是测试的命门,不是洁癖

做过持续集成的人都知道,测试最怕的不是失败,而是不稳定。一个时过时不过的测试,比一个稳定失败的测试危害大十倍。它会侵蚀团队对测试套件的信任,让人养成"这个测试偶尔不过,不用管"的习惯。

LLM 生成的 mock 数据引入了一种新的不稳定源:mock drift。数据慢慢偏离原始约束,但不会触发任何报错。你查不出测试为什么变了,因为输入没变,只是 LLM 的输出变了。

更麻烦的是数据混合问题。Expedia 的方案里,同一个响应可以同时包含真实数据和 LLM 生成数据。如果测试无意中覆盖了 LLM 生成的部分,某天 LLM 返回了不一样的值,测试就会以一种莫名其妙的方式失败。排查这种问题,你得先意识到哪些数据是 AI 造的——而响应里没有任何标记。

Schema 约束了形状,约束不了内容

Expedia 的工程师说了句很对的话:模型不擅长创造形状,但擅长填充形状。GraphQL 的强类型 schema 免费给 LLM 提供了一个边界明确的容器。

这确实是 LLM 生成数据比随机数据好用的原因。它能保证 JSON 结构正确、字段类型匹配、枚举值合法,甚至能生成语义上合理的内容。但"结构正确"和"内容确定"是两回事。

这就像让 AI 按简历模板填空。格式没问题,但每次填的工作经历都不一样。对于只需要展示效果的场景——demo、原型、内部评审——这够了。对于需要稳定性的场景——回归测试、契约测试、快照比对——这种"差不多"的数据就是定时炸弹。

RFC 的野心,和它距离现实的距离

RFC 提出了一个有意思的方向:把 mock 定义在操作级别而不是字段级别。这意味着 mock 数据的组织方式跟查询方式一致,而不是跟字段定义绑定。它还建议给 AI 编码助手提供一个 Skill,让 agent 能对话式地管理 mock 变体。

想法不错。但 RFC 目前是 Stage 0——strawman,规范流程里最早期的阶段,没有倡导者,不保证会推进。Expedia 的实现已经在指令位置、参数命名、网络行为上偏离了 RFC 草案。

如果今天有团队把 @mock 当标准来推行,实际上只是在把某一家供应商的理解当成了标准。这个风险,做技术选型时得看清楚。

为什么是 GraphQL 而不是 REST

这个现象出现在 GraphQL 而非 REST,不是偶然。GraphQL 的强类型 schema 是 LLM 生成数据的天然护栏——有 schema 就有边界,有边界就有验证。REST 的 JSON 响应通常没有这种约束,LLM 生成的数据太容易"看起来对但实际跑偏"。

更深一层看,GraphQL 的 selection set 本身就是一个规范。你查什么字段、什么类型、什么嵌套关系,全都写得清清楚楚。LLM 不需要猜,只需要填。这种确定性降低了生成出错的概率,但也只是降低——不是消除。

我的看法

LLM 生成 mock 数据的方向是对的,但当下的现实是:三家方案互不兼容,标准化遥遥无期,非确定性问题没有根本解决。

对于测试要求不高的场景——demo、原型、联调——放心用,确实能省不少写 fixture 的时间。对于需要可重复性的场景——CI/CD、回归测试、契约测试——用之前得想清楚:你能不能接受一个"偶尔会自己变"的测试数据源。

在金融业务里,答案通常是不能。你不会希望一笔理财产品的 mock 收益率每次查询都不一样。确定性不是工程洁癖,是测试的地基。AI 能写出看起来正确的数据,但"正确"和"稳定正确"之间,还隔着一段不短的距离。

You voted 1. Total votes: 12

添加新评论