接口自动化:如何设计接口断言体系

接口自动化里最容易自我感动的一件事,就是 case 跑了很多,但断言非常浅。

比如只验证:

  • HTTP 200
  • code == 0

这种 case 数量再多,也只能说明“接口看起来活着”,并不能说明业务真的正确。很多系统的线上问题,恰恰就藏在“接口成功了,但状态错了、数据没落库、缓存没刷新、消息没发出去”这种场景里。

断言体系重构时,核心目标不是多写几个 assertEqual,而是让结果能回答两个问题:

  • 这个接口到底错在哪一层
  • 这个失败是否足够值得告警

一、为什么不建议“全字段断言”

早期常见的做法,是把整个响应体拿来和基准 JSON 做比对,觉得这样最严格。实际跑起来问题很多:

  • 新增一个非关键字段就会导致大面积失败
  • traceId、时间戳、动态排序等字段天然不稳定
  • 维护者看到大段 diff,不容易第一时间定位真正风险点
  • 高噪声断言会快速削弱团队对自动化结果的信任

所以更倾向于“风险驱动断言”:

  • 核心结果做强断言
  • 契约结构做中断言
  • 非关键信息做弱断言或观测断言

二、在项目里会怎么给断言分层

通常至少拆四层。

1. 协议层断言

验证:

  • HTTP 状态码
  • Content-Type
  • 基本响应时长
  • 是否返回预期 header

这层回答的是:服务有没有正确接住请求。

2. 业务层断言

验证:

  • 业务码
  • 成功标志
  • 关键字段值
  • 状态流转

这层回答的是:业务逻辑有没有按预期执行。

3. 契约层断言

验证:

  • 字段是否存在
  • 字段类型是否正确
  • 嵌套结构是否符合约定

这层回答的是:接口契约有没有被破坏。

4. 副作用层断言

验证:

  • MySQL 是否写入
  • Redis 是否刷新
  • MQ 是否投递
  • 审计日志是否落地

这层回答的是:这次动作是否真的形成了业务闭环。

三、会用哪些工具

真实项目里,通常会组合这些工具:

  • jsonpath-ng:提取关键字段
  • jsonschema:做结构断言
  • deepdiff:做复杂对象差异分析
  • pymysql:校验数据库状态
  • redis-py:校验缓存变化
  • 自定义 AssertionResult 对象:统一输出失败分类

为什么要做 AssertionResult

  • 把断言结果结构化
  • 让报告和告警模块能消费
  • 避免控制台里只有一句“断言失败”

四、一个更接近真实业务的例子

以“创建营销任务”接口为例,不适合只写:

1
self.assertEqual(resp.json()["code"], 0)

通常会做下面这些校验:

  1. HTTP 状态码是 200
  2. code == 0
  3. 响应体中的 taskId 存在且格式正确
  4. taskName 与入参一致
  5. MySQL 中任务表新增了一条记录
  6. 新记录状态是 INIT
  7. Redis 中该任务的配置缓存已刷新

如果系统还依赖异步链路,会继续补:

  1. 消息队列里是否投递了任务创建事件

只有做到这个层次,断言才不是“证明接口调用成功”,而是在“证明业务已完成”。

五、怎么组织断言代码

更稳的做法,不是把所有断言写在 case 文件里,而是拆成:

1
2
3
4
5
6
asserts/
├── protocol_assert.py
├── business_assert.py
├── schema_assert.py
├── side_effect_assert.py
└── classifier.py

业务 case 里只保留组合关系,例如:

1
2
3
4
5
result = campaign_service.create_campaign(payload)
protocol_assert.ok(result)
business_assert.code_is_success(result)
schema_assert.match(result.json, "campaign_create_schema")
side_effect_assert.task_created(result.json["data"]["taskId"])

这样做的好处是:

  • 断言可复用
  • 断言变更集中治理
  • 失败分类天然统一

六、为什么断言必须和失败分类一起设计

如果断言失败后只是抛异常,后面报告和告警几乎没有信息量。

会让断言结果至少带这些字段:

  • assert_type
  • severity
  • actual
  • expected
  • failure_category
  • message

例如:

  • 协议层失败归类为 protocol_failure
  • 业务码失败归类为 business_failure
  • DB 校验失败归类为 side_effect_failure

这样 Jenkins 报告、邮件、企业微信消息里才能看出问题到底在哪一层。

七、断言深度怎么和执行成本平衡

副作用断言做深以后,执行时间一定会上升,所以不适合让所有任务都跑满配。

通常会这样分:

  • smoke:协议层 + 核心业务层
  • regression:协议层 + 业务层 + 契约层
  • nightly:补充数据库、缓存、副作用断言
  • production_probe:只保留低侵入强信号断言

这比“一套断言跑所有场景”更现实。

八、做完后效果怎么判断

判断断言体系有没有价值,主要看这些结果:

  • 失败后能不能快速知道是协议、业务还是副作用问题
  • 是否能减少“接口成功但业务已错”的漏检
  • 告警是否不再把低风险字段变化和核心逻辑错误混在一起
  • 新增一个类似接口时,断言模块是否可以直接复用

如果这些结果没有出现,说明断言体系大概率还停留在“代码里多写了几句 assert”。

九、结语

接口断言体系本质上是一套质量信号系统。它决定自动化是在验证“服务有没有活着”,还是在验证“业务有没有真的做对”。真正成熟的断言,不只是更严格,而是更有层次、更可解释、更能支撑后续的报告和告警。

本章延伸阅读