AI 写代码之后,测试自动化反而更重要:把它做成回归闸口

AI 写完一个功能你很高兴,两周后另一个任务改了一处公共逻辑,旧功能悄悄坏了——你直到用户报障才发现。这就是回归:缺陷不在「写的时候」,而在「改的时候」。Code Review 是时间点闸口,拦当次拦不住未来;要守未来,需要一套会一直跑的测试闸口。
先纠正一个误解:AI 时代,测试反而更重要
测试自动化在 AI 辅助编程之前,就是质量回归保障最重要的方式之一。AI 来了以后,一种声音是:「代码都不用我写了,测试还要我操心吗?AI 自己会写测试。」这个判断恰好反了——结构性变化指向的是反面:
| 变化 | 带来的后果 | 对测试自动化的影响 |
|---|---|---|
| 你不再逐行写代码 | 以前你亲手敲下的每一行,脑子里都留着一张「哪些地方会受影响」的隐性依赖图;现在 AI 一次性甩给你一大段 diff,连它动了哪些公共函数都得重读一遍 | 掌控力下降,而自动化闸口是拿回掌控权成本最低的方式 |
| AI 的改动面更大、更自信 | 一次任务横跨十几个文件,AI 不会像人一样「顺手改两行就心虚」 | 回归面积变大,人肉 review 兜不住 |
| AI 很擅长让测试变绿 | 它会把被测对象 mock 掉、把断言抄成实现、写只跑不断言的冒烟测试,然后告诉你「全部通过」 | 「全绿」的欺骗性上升,没有规约就会拿到一套虚假安全感 |
一句话:AI 把「写测试」的成本打下来了,同时把「不写测试」的风险抬上去了。
选型:三层分层,e2e 极度克制
给一个已写完/将写完的任务补测试,三种走法:
| 方案 | 优点 | 缺点 | 适合 |
|---|---|---|---|
| 不写测试,全靠人肉 review | 零额外成本 | 只拦当次,拦不住未来回归 | 一次性、极低风险改动 |
| 只写 e2e | 最贴近用户视角 | 慢、脆、维护贵 | 核心链路极少的关键业务 |
| 三层分层(unit + integration + 少量 e2e) | 各层互补,回归面全覆盖且成本可控 | 需想清楚每层边界 | 会持续演进的功能/模块 |
最终选三层分层,但 e2e 极度克制:单测与集成构成日常「一直跑」的主体,e2e 只罩住「用户绝对不能丢」的 1–3 条关键路径。
- unit:纯函数 / 纯逻辑 / 边界条件。最快最便宜,覆盖正常 + 边界输入(空、越界、异常分支)。
- integration:跨模块、跨外部依赖的契约——DB 读写、API 契约、CLI/GUI 双端一致性、消息格式。
- e2e:仅关键用户路径,用便宜模型在 CI 跑,数量 ≤ 关键路径数。
关键增量:人做规划,AI 做执行
把「补测试」拆成两层看:
- 执行层:写一个能跑的 unit test、补参数化用例、处理 fixture、修 lint。这一层 AI 已经做得很好,甚至比多数人写得快、写得规范——交给 AI,不要自己动手。
- 规划层:这个模块要测哪些面?哪条是关键路径?多少 e2e 算够?什么样的测试才算「有效」?AI 默认不知道,它只会按「看起来合理」去猜,结果就是覆盖面随机、边界遗漏、假绿横行。
所以真正的杠杆是:人把规划写死成一份规约,AI 照着执行。 规约解决两个词——全面与可控。
全面 = 覆盖面清单,不让 AI 自由发挥
不要说「给这个函数补点测试」,要说「按这 7 条逐项补,缺一项在 PR 里说明为什么」:
- 正常输入 → 明确的期望值(不是「跑通」)
- 边界输入:空 / 0 / 1 / 最大值 / 越界 / 极大极小
- 非法输入 → 断言具体的异常类型与消息
- 失败路径:依赖超时、部分失败、重试耗尽
- 幂等性:重复调用结果一致
- 无状态残留:用例可独立、乱序、并发执行,不污染 DB / 文件 / 全局
- 契约不变:跨模块/外部依赖的字段名、顺序、错误码、消息格式
可控 = 测试必须能失败(这一条最关键)
一条不会失败的测试,不是保障,是装饰。AI 特别容易产出这类「假绿」测试,要用红线挡住:
- 变异自检(mutation sanity):交付前把被测实现故意改坏一小处(翻转判断、删掉范围校验、改一个常量),测试必须变红。还绿 = 没守住任何东西,重写它。
- ❌ 禁止无断言 / 只
assert True/ 只打印 - ❌ 禁止 mock 掉被测对象本身(只允许 mock 外部依赖)
- ❌ 禁止断言抄实现(
assert result == impl(x)这类同义反复) - ❌ 禁止用
sleep(秒)等异步结果(注入时钟或显式等待)
测试规约:可贴进仓库的 TESTING_GUIDELINE.md(节选)
# TESTING_GUIDELINE.md (补测试前必读,AI 与人都遵守)
## 分层与配额
- unit 70% / integration 20% / e2e 10%(按用例数,不按重要性)
- e2e 只覆盖 1–3 条「用户绝对不能丢」的关键路径,新增需写明理由
## 覆盖面清单(每个被测单元逐项回答,缺项在 PR 里说明)
1. 正常输入 → 明确期望值
2. 边界:空 / 0 / 1 / 最大 / 越界 / 极大极小
3. 非法输入 → 断言具体异常类型与消息
4. 失败路径:依赖超时、部分失败、重试耗尽
5. 幂等:重复调用结果一致
6. 无状态残留:可独立、乱序、并发执行
7. 契约不变:字段名 / 顺序 / 错误码 / 消息格式
## 有效性红线(违反即打回)
- ❌ 无断言 / 只 assert True / 只打印
- ❌ mock 掉被测对象本身(只允许 mock 外部依赖)
- ❌ 断言直接抄实现(同义反复)
- ❌ sleep(秒) 等待异步结果
- ✅ 交付前做变异自检:改坏实现 → 测试必须变红,否则重写
核心代码:断言式单测覆盖边界
import pytest
def price_after_discount(base: float, pct: float) -> float:
if pct < 0 or pct > 1:
raise ValueError("pct must be in [0, 1]")
return round(base * (1 - pct), 2)
def test_price_boundary():
assert price_after_discount(100, 0.1) == 90.0 # 正常
assert price_after_discount(100, 0.0) == 100.0 # 边界:无折扣
assert price_after_discount(100, 1.0) == 0.0 # 边界:全免
for bad in (-0.1, 1.1): # 非法输入
with pytest.raises(ValueError): # 显式断言异常被抛出
price_after_discount(100, bad)
关键规则:最后一组不是「跑通就好」,而是显式断言异常被抛出——无断言的「跑通」不算守住边界。
变异自检:证明这条测试真的会失败
def price_after_discount(base: float, pct: float) -> float:
- if pct < 0 or pct > 1:
- raise ValueError("pct must be in [0, 1]")
+ pass # 变异:故意删掉入参校验
return round(base * (1 - pct), 2)
# 期望:test_price_boundary 必须 FAIL(红色)
# 若仍然 PASS → 这条测试是「假绿」,重写它,而不是把实现改回去就算完。
这一步是「可控」的落地:它把「我觉得测了」变成「我证明过它会拦住这个错误」。
强制执行的规则
分层与流程:
- 测试与代码同 PR 进:质量闸口前置,不攒到发布前才补。
- e2e 数量克制:关键路径有几条就写几条。
- 闸口全绿才进 review:机器先过,再交独立 review。
- 跑测试用便宜模型/CI 资源(呼应 A2 模型分层):不占用高端主力额度。
给 AI 的约束:
- 先有规约,后有测试:没有 TESTING_GUIDELINE.md 就别让 AI 自由发挥补测试。
- 绝不写「无断言」的测试:能打勾的断言才是测试。
- 交付前必做变异自检:改坏实现测试必须变红,不变红就是假绿。
- 禁止 mock 被测对象本身:只允许 mock 外部依赖。
效果与收益(定性)
- 回归率:CI 闸口在合并前拦下回归,不再等用户报障。
- 掌控感回归:你不逐行写代码了,但每次改动都有机器替你复述「哪些既有行为还没被弄坏」。
- 测试质量可信:变异自检把「AI 写的一堆绿测试」筛成「证明过能拦错的一批」。
- review 负担:机器先过常规回归,独立 review 聚焦设计/一致性盲区,两边都更省力。
- 成本透明可控:生成用平价模型、执行走 CI,几乎不挤占高端主力额度。
复现要点
- 在仓库根目录落一份
TESTING_GUIDELINE.md(分层配额 + 覆盖面清单 + 有效性红线),补测试前必读。 - 挑会持续改的功能,先列 1–3 条「绝对不能丢」的关键路径(e2e 范围)。
- 抽纯逻辑成可单测函数,按覆盖面清单补正常 + 边界 + 异常分支的断言式单测。
- 给跨模块/外部依赖契约补 integration 测试(DB、API、CLI-GUI 一致性)。
- 仅关键路径补 e2e,数量克制,用便宜模型在 CI 跑。
- 交付前做变异自检:故意改坏实现,测试必须变红;不变红就重写这条测试。
- 测试与代码进同一 PR,闸口全绿后再交独立 review。
总结与延伸
- AI 写代码之后,测试自动化比以往更重要——掌控力下降、回归面积变大、「全绿」更会骗人。
- 人做规划、AI 做执行:用一份测试规约把「写什么才全面、什么才算有效」定死。
- 三层分层、e2e 克制:unit 守逻辑、integration 守契约、e2e 只罩关键路径。
- 可控 = 测试必须能失败:变异自检是区分「真保障」与「假绿装饰」的硬标准。
- 前置 + 便宜模型跑:测试随 PR 进、执行走 CI,让补测试花得起。
前文可配合阅读:独立 Code Review 前置:把质量成本留在「写完即过」。
说明:本文为方法论述,收益为定性观察;量化指标(合并前拦下的回归缺陷数、PR 套件耗时、测试相关 credits 占比、变异自检假绿命中率)建议在实践中回填。
TESTING_GUIDELINE.md为可改写模板,分层配额与覆盖面清单需按项目形态调整。