尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

测试全绿代码却需重写?AI生成代码的评审陷阱与边界测试

测试全绿代码却需重写?AI生成代码的评审陷阱与边界测试 在实际项目中我会把 AI 编程看作一个效率工具但不会把它输出的代码当作“完成品”。很多团队之所以遇到“测试全绿代码却烂到推倒重来”的状况不是 AI 生成的代码能力太差而是验证流程把“测试通过”当成了“需求正确”。下面用一个订单折扣计算模块完整走一遍 AI 生成、测试、评审的过程看看绿灯亮起时代码里到底埋了多少问题。1. 先理解“测试全绿”为什么变成一种安全感幻觉1.1 测试通过不等于行为正确测试的本质是用有限输入验证有限行为。你写一百个用例也只能证明这一百个输入对应一百个输出是正确的不能证明第一百零一个输入也正确。AI 生成的测试尤其容易给人错觉它看起来调用了很多方法覆盖了很多行代码但每个方法可能只测了一个最普通的分支边界条件和异常分支全部缺席。真实项目里业务规则往往由 20% 的正常路径和 80% 的边界路径组成。满 500 打 9.5 折正常人会先测 600真正容易出错的是 499、500、501、1000、1000.01 和负数。AI 编程最危险的地方在于它先写了实现再根据实现反推测试于是测试天然和实现共享同一个错误预期。测试全绿不是代码健康的证据只是实现和测试达成了内部一致。1.2 AI 生成代码时“测试全绿”的三种典型情形第一种测试与实现是同一轮生成的两者来自同一份上下文。如果上下文本身有歧义测试和实现会沿着同一个错误方向演进最终一起通过谁也发现不了问题。第二种测试只覆盖主路径。补全路径、异常路径、边界路径缺失错误分支根本没有被触达。很多 AI 生成的测试会按照“传入正常金额 - 返回打折后金额”这种模式生成不会主动思考输入为空、金额为负数、折扣超上限等场景。第三种mock 过度。当被测对象的所有依赖都被 mock 掉之后测试验证的是替身不是真实行为。AI 为了快速稳定产出用例经常大量使用when(...).thenReturn(...)把关键逻辑藏在替身背后这样即使实现完全错误测试也可能继续绿灯。这三种情形有一个共同点测试报告不会给出任何提示。JUnit 输出Tests run: 4, Failures: 0, Errors: 0只会让人觉得一切正常但你的用例集本身不合格全绿就不是安全信号。1.3 判断测试质量先看三个维度判断一个测试是否合格不要先看通过率先看三个维度断言强度断言的是最终业务结果还是“返回值不为空”这类弱保证。分支覆盖是否覆盖边界值、异常值、空值、组合条件。依赖可控性使用真实组件配合少量替身还是把所有逻辑都 mock 掉。维度健康测试虚假绿灯断言目标业务结果和约束无异常、返回值非空输入范围边界值、异常值、组合场景一组正常输入依赖处理真实组件配合少量替身mock 剥掉全部逻辑失败含义能定位到具体行为偏离只能说明没有抛异常用例来源从需求规则推导从实现代码反推所以在评审 AI 生成的代码时不要只看总用例数和通过率要先看断言目标和输入集合。2. 一个典型反面案例金额计算模块从生成到推倒2.1 需求看起来很简单但是边界很多订单金额计算是典型“说法容易、实现难”的需求。假设规则如下订单金额低于 500 元不打折。订单金额大于等于 500 元且小于 1000 元打 9.5 折。订单金额大于等于 1000 元打 9 折。使用优惠券编码 VIP9 时在原价基础上额外增加 20% 折扣。非 VIP9 场景基础折扣金额上限 200 元。使用 VIP9 后总折扣金额上限 300 元。最终金额保留两位小数。这个需求看起来只有三句话实际包含至少八个行为约束1000 是否含边界、500 是否含边界、优惠券为空怎么处理、折扣封顶后最终金额是否重新计算、两档优惠叠加时封顶怎么算、金额为负数怎么处理、金额精度使用什么类型、是否保留小数位。任何一个约束没写进测试测试全绿都可能掩盖一个生产故障。2.2 给 AI 的提示词长什么样很多开发者第一版提示词是这样写的请写一个 Java 方法计算订单折扣后的金额。满1000打9折满500打9.5折VIP9再打8折折扣不能超过200总折扣不能超过300。这段提示词没有说清楚上限作用在折扣金额还是最终金额没有说输入类型没有说异常处理没有说要不要保留两位小数。AI 生成代码时只能自己填空而它填充的内容很可能和业务规则不一致。可以改成下面这种更接近“行为契约”的提示词需求订单折扣计算 输入订单金额 amount类型为 BigDecimal优惠券编码 couponCode可能为 null 输出最终支付金额类型为 BigDecimal保留两位小数 规则 1. amount 500 不打折 2. 500 amount 1000 打 9.5 折 3. amount 1000 打 9 折 4. couponCode 为 VIP9 时在原价基础上额外增加 20% 折扣 5. 非 VIP9 场景基础折扣金额上限 200 元 6. 使用 VIP9 后总折扣金额上限 300 元 7. amount 为 null 或负数时抛出 IllegalArgumentException 要求先列出测试用例再写实现和单测这不是提示词越长越好而是把业务规则从“听说”变成“可验证行为”。AI 在缺少边界约束时会默认选择最容易通过的实现方式而这个方式往往不是业务想要的。2.3 AI 生成的第一版实现存在明显问题假设 AI 在收到上面不够好的提示词后生成如下代码public class OrderDiscountCalculator { public double calculate(double amount, String couponCode) { double discount 0.0; if (amount 1000) { discount amount * 0.10; } else if (amount 500) { discount amount * 0.05; } if (VIP9.equals(couponCode)) { discount amount * 0.20; } double finalAmount amount - discount; if (discount 200) { discount 200; } if (discount 300) { discount 300; } if (finalAmount 0) { finalAmount 0; } return finalAmount; } }这段代码可以编译也可以在大部分用例下运行。问题在于discount计算出来后finalAmount已经用未封顶的折扣算完后面的if (discount 200)和if (discount 300)只修改了局部变量discount却没有重新计算finalAmount。也就是说封顶逻辑形同虚设。这段代码还有其他问题金额使用double浮点数计算存在精度误差保留两位小数的需求根本没有实现。1000、500、0.10、0.05、200、300 等魔法数字散落方法内部后续调整优惠规则需要逐个改。所有职责集中在一个方法里输入校验、折扣策略、优惠券匹配、封顶计算、最终金额计算。对空订单、负数金额、非法优惠券没有明确处理。2.4 测试代码测试全绿但没有验证关键约束对应这套实现AI 可能生成以下测试import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class OrderDiscountCalculatorTest { private final OrderDiscountCalculator calculator new OrderDiscountCalculator(); Test void orderBelow500ShouldNotDiscount() { double result calculator.calculate(300, null); assertEquals(300.
返回列表