AI 写的代码跑过 326 条用例,我为什么仍不敢合并?
一个电商结算服务准备上线“跨店优惠按商品金额分摊”。开发用 AI 完成了核心函数,也顺手生成了 326 条单元测试。流水线显示:全部通过,行覆盖率 96%。
评审时我只问了一个问题:把“最后一分钱补给余数最大的商品”改成“补给列表第一件商品”,测试会不会红?
答案是不会。因为代码和用例来自同一段提示词,二者都默认“只要总金额相等就行”,没有验证商家维度的分摊公平性。覆盖率很高,错误却能安静地活下来。

AI 生成代码改变了测试的第一个风险:同源偏差
过去我们担心开发漏写测试;现在更麻烦的是,AI 可以同时生成实现、测试数据和预期结果。产物看起来完整,却可能共享同一个错误前提。
例如实现用浮点数计算优惠,测试也调用同一个 rounding helper 生成 expected。即使舍入规则错了,实际值与预期值仍会一起错。测试通过只能说明两段代码意见一致,不能说明它们符合财务规则。

先写业务不变量,再允许 AI 写例子
优惠分摊最重要的不是列 326 组输入,而是把不会随实现变化的规则钉死:
- 每个商品优惠不得小于 0,也不得超过商品金额;
- 分摊后的优惠之和必须精确等于订单总优惠;
- 商品顺序改变,不应改变同一 SKU 的分摊结果;
- 最后一分钱按明确、可审计的规则分配;
- 金额计算使用十进制定点语义,不能把二进制浮点误差带入账务。
下面是一段可直接放进项目的独立校验。它不复制分摊算法,只验证最终业务状态:
from decimal import Decimal
CENT = Decimal("0.01")
def assert_allocation_contract(items, total_discount, result):
by_sku = {row["sku"]: Decimal(str(row["discount"])) for row in result}
assert set(by_sku) == {item["sku"] for item in items}
assert sum(by_sku.values(), Decimal("0")) == Decimal(str(total_discount))
for item in items:
discount = by_sku[item["sku"]]
price = Decimal(str(item["price"]))
assert discount >= Decimal("0")
assert discount <= price
assert discount.quantize(CENT) == discount
def assert_permutation_invariant(allocate, items, discount):
normal = allocate(items, discount)
reversed_result = allocate(list(reversed(items)), discount)
left = {x["sku"]: x["discount"] for x in normal}
right = {x["sku"]: x["discount"] for x in reversed_result}
assert left == right
这里故意没有断言“第一个商品应该分到 3.27 元”。因为具体结果可能随合法算法调整;不变量负责守住账务底线,少量经过业务确认的黄金样本再负责锁定监管或合同口径。
用变异测试检查:你的测试到底会不会抓错
变异测试会自动对生产代码做小幅破坏,例如把大于号改成大于等于、删除一个条件、把加法换成减法,再运行测试。测试失败,说明这个错误被“杀死”;测试仍绿,说明断言存在盲区。
对这段优惠逻辑,至少应手工设计四类变异:
- 把 ROUND_HALF_UP 改成 ROUND_DOWN;
- 删除“优惠不得超过商品金额”的上限;
- 把余数排序从降序改为升序;
- 在零金额商品上继续参与分摊。
可以把工具输出简化成一个团队能读懂的门禁:
CRITICAL_MUTATIONS = {"ROUNDING", "BOUNDARY", "AUTH", "MONEY_SUM"}
def mutation_gate(report: list[dict]) -> None:
survived = [m for m in report
if m["status"] == "SURVIVED"
and m["category"] in CRITICAL_MUTATIONS]
if survived:
detail = ", ".join(m["location"] for m in survived[:5])
raise AssertionError(f"关键业务变异仍存活:{detail}")

326 条测试应该怎样重组
我更愿意看到这样的报告:18 条财务不变量全部通过;12 个经过业务确认的黄金样本通过;金额模块 47 个关键变异全部被杀死;PostgreSQL 真库下的并发与精度测试通过;AI 生成的其余样本作为探索补充。
这时 326 才不是一个装饰数字。
AI 让写代码和补测试都变快了,但“生成得多”不能替代“证据独立”。未来测试工程师最值钱的工作,不是继续与 AI 比谁写的用例多,而是设计一套让错误无处藏身的验收机制:先定义不可妥协的业务性质,再用独立预言机、变异和真实依赖去反证。
- 点赞
- 收藏
- 关注作者
评论(0)