让 Codex 写测试:先问这条测试能发现什么错误 - AIPlan

AI 生成测试时怎样保证有意义?从用户行为、边界和回归风险选样本,验证测试能捕获问题,区分真实覆盖与重复实现。

让 Codex 写测试:先问这条测试能发现什么错误 - AIPlan
一条测试的价值等于它能发现的错误数量。写之前先回答一个问题:如果我故意把这段代码改坏,这条测试会不会失败?答不上来,这条测试只是把实现抄了一遍。截至 2026 年 10 月 18 日核对,这条判断标准比覆盖率数字有用得多。 测试抄实现是最常见的问题 让它给一个计算函数写测试,常常得到这样的结果: def test_discount(): assert calculate_discount(100, 0.2) == 80 这条测试能通过,也能在你把 0.2 改成 0.3 时失败。但如果你把整个函数逻辑改成 return price * 0.8,测试照样通过。它测的是这一个输入的结果,不是规则。 改法是把规则写出来: def test_discount_never_exceeds_price(): for rate in [0, 0.1, 0.5, 0.99]: assert 0 <= calculate_discount(100, rate) <= 100 四条输入覆盖了边界,任何让结果超过原价的改动都会被抓住。 三类输入值得优先写 选样本的优先级很清楚: • 边界值:0、-1、空字符串、空列表、最大长度、最大金额 • 用户能触发的异常:余额不足、库存为 0、优惠券过期、重复提交 • 最近改过的地方:过去 3 个月修过的 bug 都补一条 第 3 条是回报最高的。一个修过的 bug 说明这条路径曾经出过问题,回归的概率比随机路径高。 第 1 条里的边界值有个通用清单:数字取 0、1、-1、上限、上限加 1;字符串取空串、1 个字符、超长;列表取空、1 项、超大。5 类乘 5 类,能生成 25 个基础样本,实际挑 8 到 10 个就够。 覆盖率的数字意义有限 行覆盖率 80% 意味着 80% 的代码被执行过,不意味着 80% 的行为被验证过。一个函数可以每行都跑到,但所有断言都只检查“不抛异常”。 判断真实覆盖的方式:把被测函数里的每个 if 分支列出来,每个分支至少一条测试。分支数比行数更能反映测试的有效性。 如果项目已经在跑覆盖率工具,可以把它当筛查工具用:覆盖率低的模块优先补,覆盖率高的模块抽查断言是否有效。别把覆盖率当 KPI,那会催生一堆没有断言的空测试。 让 Codex 自己验证测试有效 这条做法可以显著提高产出质量:写完测试后,让它故意改坏一处实现,跑测试,确认测试失败,再改回来。 任务描述: 写完测试后做一次验证: 1. 把 validateOrder 里的 `if amount <= 0` 改成 `if amount < 0` 2. 跑测试,确认有测试失败 3. 把改动恢复 4. 再跑一次,确认全部通过 把两次的输出贴出来。 能通过这个验证的测试才真的在守着行为。跑不通说明测试写得太松。 测试文件不能为了让代码通过而改 有一类失败模式很隐蔽:代码改错了,测试也改错了,两边一起变得“正确”。常见于让模型同时改实现和测试的任务。 防线是分开两轮: • 第一轮只写测试,不改实现。跑一遍,确认该失败的失败、该通过的通过。 • 第二轮改实现,测试文件不动。 如果第二轮必须改测试,先停下来问为什么。三种可能:需求变了(合理,但要走单独一轮)、测试写错了(合理,但要说明)、代码改错了在迁就(危险)。 /review 有个 Review uncommitted changes 范围,能一次看清实现和测试的改动比例。测试改动超过实现改动的时候,值得逐行看。 测试命名要写出行为 test_order_1、test_case_2 这类命名三个月后没人看得懂。改成一句话描述行为: 重复提交同一订单返回相同结果,不产生第二条记录 优惠券过期时返回 409 而非 400 金额为 0 时不创建订单 名字里带具体条件,失败时输出一行就能知道哪条规则破了。很多测试框架会把测试名直接打到失败日志里,命名质量直接决定排查速度。 一条测试只验一件事 一条测试里断言 8 个字段,失败时你不知道是哪个字段的问题,还得逐行读。拆成 3 条各验一组,失败信息直接指向问题。 例外是“整体结构”类的断言,比如返回 JSON 的 schema 校验,这种本来就该一次性验完。判断标准:失败时你需要几个信息才能定位,超过 1 个就拆。 回归测试怎么写进流程 修 bug 的标准动作是“先写一条能复现的失败测试,再修到它通过”。这条测试天然成为回归测试,因为它在修复前一定是红的。 实际执行顺序: • 用那四步复现格式把 bug 写清楚 • 写一条测试,跑起来,确认失败 • 修实现 • 再跑,确认通过 • 打检查点 第 2 步是关键。跳过它直接修,修完补的测试可能只是“把当前行为写下来”,抓不住原来那个问题。修 bug 的完整流程在 用 Codex 修 bug:先验证原因,再动实现。 什么时候不写测试 三种情况写了也没用: • 一次性的数据迁移脚本,跑完就删 • 纯配置文件的格式调整 • 界面纯样式改动,没有逻辑分支 反过来,下面这些即使看起来简单也值得写:金额计算、状态流转、权限判断、去重逻辑。这四类出问题时的代价远高于写测试的成本。 一次规模和时间 给一个 300 行的业务模块补测试,典型分配: • 读代码列分支:20 分钟 • 写 8 到 12 条测试:40 分钟 • 做故意改坏的验证:15 分钟 • 修测试本身的错误:15 分钟 合计 90 分钟。写测试的时间通常比预想的长,写的过程本身就会暴露实现里没处理的边界。 这个工作量用 Codex 做,读取集中在 1 到 2 个文件,额度消耗很低。Plus 的 $20 折算约 ¥133.80,一周给 3 到 4 个模块补测试不成问题,¥115.50 的 Plus 月卡 走的是同一条通道。测试和重构经常一起做,那部分的任务组织方式在 用 Codex 重构代码:替换旧实现,也要清理旧路径。 下次让 Codex 写测试时,先加那句“改坏一处,确认测试失败”。省下的不是写测试的时间,是三个月后排查回归的时间。 参考资料资料核对日期:2026-10-17 • Codex CLI • Code review 延伸阅读• 用 Codex 修 bug:先验证原因,再动实现 • 用 Codex 重构代码:替换旧实现,也要清理旧路径 • 用 Codex 做代码审查:每个发现都要说明怎样触发 • 用 Codex 重做前端,先列清楚页面和交易流程