Appearance
用 Codex 补测试与建立验证门槛
嘿,朋友!你有没有过这种体验——让 AI 帮你写测试,跑完一看覆盖率蹭蹭往上涨,心里还挺美,结果上线该崩还是崩?我就踩过这个坑,花了一下午让 Codex 给一个订单模块补了三十几个测试,行覆盖率接近 90%,结果第二天一个折扣计算的边缘 case 照样漏了。后来我才明白,问题出在哪儿:那些测试大部分只是把代码逻辑又抄了一遍,并没有真正验证用户会碰到的行为。
这篇跟你聊聊我怎么和 Codex 配合,写出那种真能拦住 bug 的测试,以及怎么搭一套从轻到重的验证流程,既省时间又不漏问题。
第一步:先读测试约定
每次进一个新仓库,别上来就让 Codex 写测试。先让它摸清楚这个项目的测试套路:
text
只读分析当前项目的测试体系:
- 使用哪些框架和命令;
- 单元、集成、端到端测试分别放在哪里;
- fixture、mock、数据库和浏览器怎样初始化;
- CI 实际运行哪些检查;
- 找两个与目标代码最相似的高质量测试作为范例。
不要修改文件。然后记得让它把命令的具体出处指出来——package.json、构建文件或者 CI 配置都行,别凭经验瞎猜。我有一次偷懒跳过这步,Codex 用 Jest 写了一堆测试,结果那项目用的是 Vitest,光迁移就费了半天劲。
第二步:先写测试意图
这一步是我现在最看重的。你直接说「把 calculateTotal 覆盖了」,Codex 大概率给你整一堆对着实现照抄的断言。反过来,你先把要验证的行为列清楚,效果完全不同。
拿「价格计算」举个例子,差的意图是「覆盖 calculateTotal」,好的意图长这样:
- 正常商品合计正确;
- 数量为零时不产生负数;
- 折扣只作用于允许的商品;
- 金额使用项目规定的舍入方式;
- 无效输入返回现有错误类型。
用这个提示就够了:
text
为 [行为] 设计最小测试集合。先列出每个测试防止什么回归,再写代码。优先验证公开行为,不要绑定私有函数、调用次数或无关 DOM 结构。你会发现,列完意图之后,Codex 写出来的测试质量高出一大截——因为它被迫先想「这个测试要防什么」,而不是「这段代码怎么写的」。
第三步:确认测试在修复前会失败
这一点说起来简单,做起来太容易跳过了。一个回归测试如果一上来就绿了,那它根本没资格叫回归测试——它连「有 bug」和「没 bug」都分不出来。
我的习惯是让 Codex 先把测试跑挂了再说:
text
先只添加回归测试并运行它。确认它因目标 Bug 失败,而不是因为语法、fixture 或环境错误。把失败断言摘要给我,然后再修改生产代码。如果测试一开始就通过,赶紧排查这几样:
- 没有触发真实问题;
- 断言太弱;
- 测错了层;
- 当前分支已包含修复;
- 复现条件缺失。
上周我修一个分页 bug 就碰到这种情况——测试直接绿了,查半天才发现是因为测试数据只有 3 条,根本没触发分页逻辑。把数据加到 20 条之后才如愿挂掉。
第四步:避免无价值测试
以下这些反模式,我几乎每一个都亲身踩过,说出来都是泪:
- 只断言函数被调用,没有断言结果;
- mock 掉了真正出错的层;
- 测试复制生产实现的计算步骤;
- 快照巨大且无人审查;
- 通过
sleep等待异步行为; - 为通过测试改变生产语义;
- 捕获异常但没有断言类型和内容;
- 只覆盖成功路径。
说实话,上面这些我自己写测试的时候也犯过,尤其是「mock 掉真正出错的那层」——把数据库 mock 了,把网络 mock 了,把一切可能出错的东西都 mock 了,最后测了个寂寞。
好在可以让 Codex 帮你自审一轮:
text
审查刚添加的测试:它们是否会在实现退化时真正失败?指出过度 mock、恒真断言、重复实现和时序不稳定风险,并做最小调整。第五步:建立验证阶梯
千万别每改一行就跑一次完整 CI,浪费时间不说,还容易让你对跑测试这件事产生心理阴影。反过来,也别只跑了个单测就信心满满地合并。
我现在的习惯是按照成本从小到大跑:
- 单个新增测试;
- 当前测试文件;
- 当前模块或包;
- 类型检查/lint/编译;
- 相关集成或端到端测试;
- 项目完整构建。
提示模板:
text
按成本从低到高列出本次改动的验证阶梯。先运行最小项;只有通过后才扩大。每一步报告命令、退出码和关键统计,失败时停止并定位。这套阶梯流程用熟了之后真的很舒服——大部分问题在前三步就被拦住了,根本不需要等二十分钟的 CI。
测试 UI
UI 测试最容易写出又脆又没用的东西,分层来搞会清晰很多:
- 纯函数和状态转换用单元测试;
- 组件交互用组件测试;
- 路由、接口和关键用户旅程用端到端测试;
- 视觉细节用浏览器人工检查或视觉回归。
核心原则:按钮能不能点、表单能不能提交、错误能不能显示——这些才是用户真正关心的。第几个 div 的问题属于过度耦合,改一下 DOM 结构就全崩了,维护成本巨高。
处理不稳定测试
flaky test 大概是测试领域最磨人的东西了。先别急着加 retry,把失败模式搞清楚再说。重复跑几遍,记录规律,然后逐一排查:
- 共享全局状态是否清理;
- 时间和时区是否固定;
- 随机数是否设置种子;
- 异步任务是否有明确完成信号;
- 端口、文件、数据库是否隔离;
- 测试顺序是否影响结果;
- 外部服务是否应该替换为可控测试替身。
有一说一,禁止只加重试次数就宣布解决。重试能临时兜一下环境抖动,但你得把可观测性留着——到底为什么 flaky,得能追溯。
测试数据与隐私
用明晃晃的虚构数据,别偷懒。生产快照、真实账号、订单号、日志、Access Token 统统别往 fixture 里塞。真要参考真实数据形状的话,先最小化字段再脱敏,安全第一。
一份合格的测试报告
text
新增测试:
- [测试名]:防止 [具体回归]
修复前:
- [命令]:失败,原因是 [目标断言]
修复后:
- [单测命令]:通过
- [模块命令]:通过
- [构建命令]:通过
未运行:
- [端到端测试]:缺少 [环境]
风险:
- [尚未覆盖的场景]完成门槛
- [ ] 测试意图对应真实行为或业务规则。
- [ ] 回归测试在修复前能因正确原因失败。
- [ ] 没有通过过度 mock 隐藏目标问题。
- [ ] 从最小测试扩大到合适的项目检查。
- [ ] 测试数据不包含真实敏感信息。
下一步
界面任务继续学习 根据截图实现和迭代界面;大型结构调整进入 可回退的重构与迁移。