一个AI编程智能体接到需求,写完功能代码,生成测试,跑一遍,然后提交一个构建通过的合并请求。
这看起来像是独立验证。但它往往不是。
![]()
如果实现代码和测试用例来自同一个提示词、同一份仓库上下文、对需求的同一种理解,那么它们可能重复同一个错误。代码是错的,测试却信心十足地证明这个错误的行为完全符合实现。
问题不在于AI写不出测试。问题在于,把相关性极高的输出当成了独立证据。
一个账单需求,两种公式,同一个结果
设想一条账单需求:先对订单小计打八折,再对折后金额计税。
预期计算是:小计100.00,折扣20.00,应税金额80.00,税额8.00,最终总额88.00。
但智能体可能误读需求,把折扣放到计税之后:先算税,再减折扣。对于上面这组简单数值,两种公式碰巧都返回88.00。于是生成的测试通过了。
这个测试看起来合理,甚至用了一个具体的期望值。但它无法区分预期公式和错误公式。
一旦引入固定折扣、免税额度、按地区取整,或者不同税率的商品类别,这个被共享的假设就会变成真实的生产账单缺陷。
覆盖率回答不了正确性
一套生成的测试可以达到100%行覆盖率,同时只是把实现当前的逻辑原样断言回去。
有一种模式尤其脆弱:测试里重新构造了实现所用的同一个公式。如果公式本身是错的,两边就一起错下去。
测试需要一个判定基准——一个独立的、相信期望结果正确的理由。
当同一个智能体在同一会话里既写代码又写测试,两份产物可能继承同样的偏差。在同一个对话里重新生成测试,未必能带来有意义的独立性,因为模型仍然看得到自己先前的推理和实现。
换第二个模型也不自动够用。如果两个模型拿到同样含糊的提示词,又从同一份实现推导期望,它们可能收敛到同一个看起来很美的错误上。
目标不只是换一个生成器,而是换一个真相来源。
像 expect(formatPrice(input)).toBe(formatPrice(input)) 这种极端例子一眼就能看穿,但间接版本很常见:测试调用了和生产路径相同的辅助函数、解析器、映射器或公式。
快照测试也是同理。第一个快照记录的是实现产出的任何东西。批准它,只证明有人接受了这个输出,不证明输出符合需求。快照擅长检测变化,但在没人核实其含义时,它很难确立初始正确性。
还有一类测试确认的是结构而非可观察行为。它可能通过,用户却拿到过期数据;也可能在一次安全重构中失败。
如果问题描述里只给了一个例子,智能体往往就把它复现成一个顺利路径测试。而那个例子,未必能区分两种相互竞争的理解。
用模拟对象把依赖固定成永远瞬间成功,测试证明的只是这个依赖永远成功时的行为。它对超时、重复响应、结果不确定、远端已处理后的重试,什么也没说。
从举例到不变量
例子验证的是选定的点,不变量描述的是必须在大量输入下都成立的属性。
对于折扣计算,有用的不变量可能包括:折扣增加时总额不应增加。
基于属性的测试可以在生成的输入上检验这类规则。比如用 fast-check 配合 vitest,随机生成小计、税率和两个折扣率,取较小折扣和较大折扣分别计算总额,断言折扣更大时总额不会更高。
这仍然不能证明所有业务规则,但它让某一个方便的例子更难蒙混过关。
AI写测试本身不是问题。把同一来源的代码和测试当成彼此独立的证据,才是真正危险的地方。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.