测试人 Skill 全家桶 · 第③篇
前两篇,我们把需求拆成了测试点、又把测试点变成了可执行用例。 但用例再准,也得有人跑。这一篇讲:怎么把接口用例变成 pytest、断言怎么分四层、怎么接进 PR 门禁,让机器替你守。
一、先看一个你肯定经历过的场景
【贴图 01】
版本要发,你手里 60 条接口用例。
-
要么手工点:登录、拿 token、改参数、点发送、拿响应、肉眼比对……一轮下来半天没了,还点漏了两条;
-
要么写了个脚本:地址硬编码、每次跑都往库里灌垃圾数据、换个环境全挂、只断言了个
status_code == 200。
上线后出了个接口 bug,你翻脚本才发现——它压根没校验业务码。200 是拿到了,code = 5001(参数非法)它也没管。
【贴图 02】
问题不是"没写自动化",是写成了"能跑但没用"的自动化:跑得挺欢,一条真缺陷都拦不住。
一句话:
自动化不是"把点鼠标换成跑脚本",是"让机器替你守门"。
守不住门的脚本,只是把加班从手工搬到了改脚本。
二、接口自动化的四根柱子
【贴图 03】
这个 Skill 把接口自动化拆成四根柱子,缺一根都会塌:
| 柱子 | 要解决的问题 | 反面 | | --- | --- | --- | | 契约 | 测什么、参数长啥样 | 靠记忆、靠口口相传 | | 断言分层 | 怎么判"过了" | 只断言 200 | | 数据 fixture | 数据从哪来、到哪去 | 硬编码、不回收 | | CI 分层 | 什么时候跑、谁挡门 | 全量每晚跑,门禁是空的 |
三、第一根柱子:契约先行,不靠记忆
【贴图 04】
写接口用例第一步永远是:找到这份接口的契约——OpenAPI / 抓包 / 接口文档,三选一,并且写进用例注释里。
契约要拆出这些:method、path、headers、body schema、成功状态码、业务码。
为什么强调"契约来源"?因为 AI 会编参数。你让它写个接口用例,它可能顺手给你编一个
userId=123、token="Bearer xxx"——看着像真的,全是假的。给它契约,它才不猜。
四、第二根柱子:断言分四层(最容易被偷懒)
【贴图 05】
只断言 status_code == 200,是接口自动化最大的谎言。
200 只说明"HTTP 通了",不代表"业务对了"。正确的断言要分四层,从外到内:
① HTTP 状态码 → r.status_code == 200
② 响应结构 → set(body) >= {"code", "data"}
③ 业务码 → body["code"] == 0
④ 关键字段值 → body["data"]["threshold"] == 10000
真实例子(优惠券查询接口 GET /coupon/available):
@pytest.mark.smoke
def test_coupon_available_ok(coupon_client, seed_user):
r = coupon_client.get("/coupon/available", params={"userId": seed_user.id})
assert r.status_code == 200 # ① HTTP 层
body = r.json()
assert set(body) >= {"code", "data"} # ② 结构层
assert body["code"] == 0 # ③ 业务码层
assert body["data"]["threshold"] == 10000 # ④ 关键字段层(满 100 元)
【贴图 06】
四层里,第 ③④ 层才是真正在测业务。前两层只是"门没塌",后两层才是"货对不对"。
五、第三根柱子:参数化 + fixture(数据要"来有影、去有踪")
【贴图 07】
等价类和边界值,不要手抄 8 遍用例,用 @pytest.mark.parametrize:
@pytest.mark.regression
@pytest.mark.parametrize("amount,expect_code", [
(9999, 0), # 未满门槛:正常返回,无可用券
(10000, 0), # 恰好满门槛:边界
(10001, 0), # 超出门槛
(-1, 4001), # 非法金额:业务码报错
])
def test_coupon_threshold_boundary(coupon_client, seed_user, amount, expect_code):
r = coupon_client.get("/coupon/available", params={"userId": seed_user.id, "amount": amount})
assert r.json()["code"] == expect_code
数据从哪来?fixture 生成,自动回收:
@pytest.fixture
def seed_user(db):
uid = db.create_user(balance=10000)
yield uid
db.delete_user(uid) # 跑完必回收,不污染下游
两条铁律:不写死测试数据、跑完必须清理。否则你的自动化就是在给测试库倒垃圾。
六、第四根柱子:接进 CI,把门关上
【贴图 08】
自动化不进 CI,等于白写。这个 Skill 给的是三层策略:
-
标记分层
:
@pytest.mark.smoke(秒级,每次 PR) /@pytest.mark.regression(每晚); -
失败留证据
:失败时打印 request / response,CI 里一看就知道哪层挂了;
-
超时与重试
:只对网络类错误重试,业务失败绝不重试(否则把真 bug 重试没了)。
一个最小门禁配置(GitHub Actions):
- name: 接口冒烟
run: pytest -m smoke --maxfail=1 -q
【贴图 09】
还有两个隐形的坑,一并堵上:token 复用(在 conftest 里登录一次,别每个用例都登)、环境地址走配置(读环境变量,别硬编码)。
七、同一个接口:裸问 vs 用 Skill
【贴图 10】
拿 GET /coupon/available 当输入。
裸问 AI 给的(跑得欢,没用)
def test_coupon():
r = requests.get("http://test.xxx.com/api/coupon/available")
assert r.status_code == 200
问题:地址硬编码、没鉴权、只断言 200、没参数化、不清理数据。
用「接口自动化 Skill」给的(节选)
-
契约解析 → 用例骨架(正向 / 边界 / 异常 / 权限)
-
@parametrize覆盖金额边界(9999 / 10000 / 10001 / -1)
-
四层断言
逐层校验
-
fixture造数 +
yield回收 -
token 在
conftest复用 -
@pytest.mark.smoke / regression分层,接 PR 门禁
同一件事,前者是"能跑",后者是"能守门"。
八、这个 Skill 长什么样
工作流程(6 步):
① 契约解析:method/path/headers/body/状态码/业务码
② 用例骨架:正向 + 边界 + 异常 + 权限
③ 参数化:parametrize 覆盖等价类与边界
④ 断言分层:状态码 → 结构 → 业务码 → 关键字段值
⑤ fixture:数据准备 + 自动回收(yield)
⑥ CI 接入:标记分层 / 失败重试 / 报告留档
检查清单(核心资产):
· 一个接口一份契约来源,不靠记忆
· 断言分 4 层,至少覆盖「业务码 + 关键字段」
· 测试数据由 fixture 生成并回收
· token 复用,不每个用例登录一次
· 失败时打印 request / response(可定位)
· 用例幂等,可重复执行
· 打标记:@pytest.mark.smoke / regression
· 超时与重试策略明确(只重试网络类错误)
· 环境地址走配置,不硬编码
硬性规则:不写死测试数据 / 不在用例里写业务判断(逻辑走断言)/ 每次跑完必清理 / 断言必须含业务码或关键字段。
反模式:❌ 只断言 status_code == 200 ❌ 用 sleep 等异步结果 ❌ 用例依赖执行顺序 ❌ 地址账号写死。
九、落地效果 + 踩过的坑
效果(团队自测,仅供参考):
-
冒烟用例进 PR 门禁:8 分钟内给出结果,坏改动挡在合并前;
-
接口回归:约 1.5 天 → 约 40 分钟(参数化 + 分层,不用全量跑);
-
接口层漏测缺陷:显著下降(业务码层断言立了大功)。
四个坑(一定要看):
-
AI 爱只断言 200
——必须显式要求"断言分四层,业务码必检";
-
AI 爱用
sleep等异步——改成轮询 + 超时上限,或让接口提供回调/查询;
-
数据不回收 → 污染下游
——
fixture一律yield+ 清理,宁可多写两行; -
重试滥用
——只重试网络类错误;业务失败重试,等于把真缺陷藏起来。
🎁 彩蛋公众号后台回复「skills」,领取《测试人 Skill 全家桶》:本文 api-automation SKILL.md 完整版 + 10 个 Skill + 适配手册。 👉
下篇预告:《UI 自动化总在"等元素",到底该等多久?》—— 接口稳了,UI 层才是真磨人。下一篇讲:Playwright 怎么写稳、定位为什么优先 data-testid、以及"自愈"到底能救回多少条。