接口自动化平台设计:从"跑脚本"到"用例资产"

接口自动化做到第三年,最大的反思是:脚本数量从 200 涨到 2000 之后,"能跑"和"可信"是两回事。200 个脚本的时候"全绿"就是好信号;2000 个的时候,全绿可能只是"没人敢改"。这篇讲接口平台的几个核心设计:让用例成为可度量的资产。

一、用例模型:步骤化 + 依赖声明

自由编码的脚本(pytest 直接写)灵活但不可分析。平台把用例收敛成结构化步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# cases/order/test_pay_flow.yaml
case: order_pay_flow
depends_on: # 数据依赖:平台按拓扑序准备
- data:order_with_items
steps:
- id: create
api: POST /api/orders
expect: { status: 201, "body.id": "$oid" } # 断言 + 变量捕获
- id: pay
api: POST /api/orders/$oid/pay
params: { amount: "$.order.amount" } # 引用前置数据
expect: { status: 200, "body.status": "PAID" }
- id: verify_stock
api: GET /api/orders/$oid
expect: { "body.stock_decremented": true }
teardown:
- api: POST /api/orders/$oid/cancel

结构化换来三件事:

  • 可分析:平台知道每个用例"调了哪些接口、依赖哪些数据、断言了什么"——覆盖率、脆弱度、依赖图全是数据;
  • 可重放单步pay 步骤挂了,可以只重放 pay 及其后续,不用从 create 开始;
  • 可迁移:用例是 YAML 不是代码,接口域名/环境切换是配置行为。

自由编码没有消失——探索性、一次性验证仍写脚本,验证通过的价值逻辑沉淀进结构化用例。

二、断言分级:契约断言 vs 快照断言

断言写太严(全字段快照)→ 接口任何变更都挂,团队学会"改接口先改断言",断言失去意义;写太松(只看 status)→ 断言形同虚设。两级断言体系:

1
2
3
4
5
6
7
8
expect:
status: 200 # L1 契约断言:稳定字段,永不挂
body:
status: "PAID" # L1
paid_at: { type: "datetime" } # L1 类型断言
snapshot: # L2 快照断言:全字段,允许受控漂移
ignore: ["body.trace_id", "body.server_time"]
drift_policy: warn # 漂移先告警不失败,人工确认

L2 快照的 drift_policy 是关键:接口加了个新字段、改了个字段顺序,快照会"漂移"。直接失败 = 噪音;直接忽略 = 失去意义。warn 策略把漂移变成待审清单:每周 10 分钟过一遍漂移告警,确认"预期变更"转基线,"意外变更"开 issue。断言从"门禁"变成"变更雷达",这是接口自动化最有价值也最被低估的功能。

三、覆盖率:接口自动化第一次有北极星

结构化用例让覆盖率可计算:

  • 接口覆盖:路由 × 方法 × 参数组合(有接口的 OpenAPI 规格就能算);
  • 断言覆盖:每个响应字段的"被断言次数"——server_time 从来没被断言过,说明没人关注它;
  • 错误路径覆盖:4xx/5xx 分支有没有用例打进去——大多数团队的错误路径覆盖是 0,而线上事故 80% 出在错误路径。

看板上的"接口覆盖率 92%"是虚荣指标,“错误路径覆盖率 12% → 45%” 才是真指标。平台把错误路径用例单独标记(negative: true),CI 的 smoke 档必跑全部 negative 用例——错误路径的用例通常最便宜(不需要完整数据准备)也最值钱。

四、环境数据:接口语义的 fixture

接口用例的数据准备比 UI 简单(不用造"看得见"的东西),但有自己的坑:状态机依赖。"支付"用例需要一个"已创建未支付"的订单,直接调 create 接口造——但 create 接口本身挂了怎么办?

解法是数据 fixture 分层

1
2
3
4
5
6
7
8
9
10
11
12
13
# fixtures/order.py
@pytest.fixture(scope="session")
def order_factory(api):
"""订单工厂:create + 预检(create 挂了在这里 fail,用例层不用关心)"""
def make(**kw):
resp = api.post("/api/orders", json=kw)
assert resp.status == 201, f"订单工厂失效: {resp}"
return Order(resp.json())
return make

def test_pay(order_factory):
order = order_factory(item_id="sku-001") # 数据问题在 fixture 层暴露
...

原则:fixture 是"数据能力的唯一入口",fixture 挂了 = 环境/数据层问题(归因清晰),用例挂了 = 被测接口问题。归因清晰了,"环境不好"和"产品有 bug"不再混战。

五、小结

接口平台从"脚本堆"到"用例资产"的四个设计:结构化用例(可分析、可单步重放)、两级断言(契约 + 快照漂移雷达)、三维覆盖率(接口/断言/错误路径)、fixture 归因(数据问题与产品问题分家)。脚本数量到千级之后,平台的价值不再是"跑得快",而是"每个绿的/红的都有解释"。