site logo

Marico's space

API 测试策略:可靠 API 实用指南

Others 2026-07-06 20:57:25 4

做后端开发的,估计没少被 API 的坑折腾过。字段少了一个、状态码返回错了、高并发下超时了、接口契约改了导致调用方全挂了——这些问题用临时手动测的方式,靠运气能碰上几个,但系统化地测才能真正把它们抓出来。

一套好的 API 测试策略,说白了就是回答三个问题:测什么、谁来测、什么时候跑。把快速检查和慢速套件分开,提交代码时跑轻量级检查,定时或发布前跑完整回归,既能保证覆盖率,又不让每个开发流程都卡在等测试上。

API 测试策略到底指的是什么

动手写断言之前,先把下面四个问题想清楚。

1. 测什么?

按业务风险和流量优先级来排,不是按测试难度来排。

举个例子:

  • 下单、支付、认证、用户数据相关的 API 必须高覆盖
  • 健康检查、静态查询类接口一般覆盖要求低一些
  • 对外或面向合作伙伴的 API 需要契约测试

别按"这个好测"来排,按"出问题了有多疼"来排。

2. 在哪一层测?

不是所有测试都得做端到端。

用能抓住 Bug 的最低层来测:

  • 单接口检查:状态码、Schema、字段、校验
  • 集成检查:服务间调用行为
  • 契约检查:提供方和消费方是否兼容
  • 端到端检查:核心用户流程

3. 什么时候跑?

把快的和慢的反馈分开:

  • 每次 push:功能测试、契约测试、轻量回归
  • 每天夜间:更广的集成和工作流套件
  • 发布前:压测、安全测试、全量回归

4. 什么算通过?

只检查个 200 OK 那是糊弄自己。

定义清楚这些:

  • 期望的状态码
  • 响应 Schema
  • 必填字段
  • 字段值或值域
  • 错误格式
  • 相关情况下的响应时间阈值

一个断言集的例子:

GET /api/users/42 Expected:
- status = 200
- body.id = 42
- body.email is a valid email
- body matches User schema
- response time < 500ms

为什么有策略比临时测强

临时测基本就是发个请求、瞄一眼返回、然后下一个。演示的时候这么干没问题,生产环境上就漏成筛子了。

临时测不可重复

一个检查只存在某人的终端历史记录里,别人根本没法可靠地复现。自动化测试每次跑一模一样。

临时测只盯着happy path

手动测一般只覆盖开发者预期能跑通的情况:

  • 合法 payload
  • 新鲜的 token
  • 小数据量
  • 正常用户权限

线上的 bug 往往藏在别的地方:

  • 字段缺失
  • token 过期
  • 越权访问
  • 超长列表
  • 边界值
  • 畸形 JSON

临时测撑不起规模

一个服务 40 个接口、5 个环境,每次发布就是 200 个可能的手工检查点。没人会一个一个去跑。

有策略就能把这些变成可重复的自动化:写一次,到处跑,每次改动都跑一遍。

API 测试金字塔

用测试金字塔来决定每层放多少测试。

 /\ / \ 端到端/工作流测试 (少,慢,高价值) /----\ / \ 集成/契约测试 (适量,中等速度) /--------\ / \ 单元/单接口测试 (多,快,便宜) /____________\

底层:单接口检查

每个测试只打一个接口,断言返回。

这类测试用来测:

  • 状态码
  • 响应 Schema
  • 必填字段
  • 校验错误
  • 认证失败
  • 简单业务规则

速度快、代价低、好排查。测试套件里大部分应该放在这层。

中层:集成和契约检查

这些测试验证多个服务之间对数据格式和行为是否达成一致。

用来测:

  • API 到数据库的流程
  • API 到支付渠道的流程
  • 提供方-消费方兼容性
  • 跨服务的业务逻辑

比单接口测试慢,但能抓到单接口检查漏掉的 Bug。

顶层:端到端工作流

这些跑完整的用户旅程,跨多个接口。

例子:

1. 创建订单
2. 支付订单
3. 查询支付状态
4. 验证订单状态变为已支付

这类要少。它们提供高置信度,但跑得慢、维护成本高。

常见的错误是把金字塔倒过来:慢速端到端太多,快速接口测试太少。只要低层能抓到同样的问题,就把覆盖率往下压。

测试类型一览及适用场景

完整的 API 测试策略要组合多种测试类型。

功能测试

功能测试验证接口是否按规格说明运行。

请求示例:

GET /api/users/42 HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>

有用的断言:

  • 状态是 200
  • Body 符合 User Schema
  • id 等于 42
  • email 是合法邮箱字符串

每个重要接口都应该尽早把功能测试自动化。

集成测试

集成测试验证你的 API 和真实依赖配合是否正常,这些依赖包括:

  • 数据库
  • 消息队列
  • 支付渠道
  • 内部下游服务

功能测试用 Mock 能过,接上真实依赖可能就崩了。集成测试就是补这个窟窿的。

回归测试

回归测试在每次改动后重新跑已有测试,确认本来能跑通的行为没有被破坏。

通常不需要单独写"回归测试"这一类。现有的功能测试、集成测试、契约测试持续跑起来,就成了回归覆盖。

契约测试

契约测试验证 API 提供方和消费方对请求响应格式是否达成一致。

它能抓住这类破坏性改动:

  • 字段改名
  • 字段删除
  • 类型变更
  • 枚举值变化
  • 接口下线
  • 新增必填请求字段

如果有其他团队、应用或客户在调用你的 API,契约测试是必须的。

负载和性能测试

负载测试测量 API 在并发流量下的表现。

关注这些指标:

  • P95/P99 响应时间
  • 错误率
  • 吞吐量
  • 饱和点
  • 超时行为

这些场景要跑负载测试:

  • 上线前
  • 预期流量高峰前
  • 定期跑以发现性能退化

这和功能测试是分开的,通常需要专门的工具。

安全测试

安全测试验证 API 是否会拒绝不安全的或未授权的请求。

覆盖这些:

  • 缺失 token
  • token 过期
  • 权限范围错误
  • 访问他人数据
  • 注入攻击尝试
  • 不安全输入
  • 敏感数据泄露

每个处理敏感数据的接口都需要认证和授权检查。

测试类型 能抓到的问题 运行时机
功能测试 状态码、Schema、值错误 每次提交
集成测试 服务间流程断裂 每次提交或每日
回归测试 新改动破坏了已有功能 每次提交和发布前
契约测试 接口破坏性变更 提供方每次提交
负载测试 流量下的延迟和故障 上线前和定期
安全测试 认证、注入、数据泄露 发布前和定期

正向、负向和边界用例

每个重要接口都要覆盖三类情况。

正向用例

发合法输入,期望成功返回。

例子:

POST /api/users HTTP/1.1
Content-Type: application/json { "email": "dev@example.com", "name": "Dev User"
}

期望:

  • 状态 201
  • 响应符合用户 Schema
  • 返回的 email 和提交的 email 一致

负向用例

发非法输入,期望干净失败。

例子:

  • 必填字段缺失 → 400
  • 缺失 token → 401
  • 权限错误 → 403
  • 记录不存在 → 404
  • payload 类型无效 → 400

很多真实的 API Bug 都是在负向测试里发现的。

例子:

POST /api/orders HTTP/1.1
Content-Type: application/json { "customerId": "c_123", "quantity": -5 }

期望:

  • 状态 400
  • Body 包含 quantity 的校验错误

如果这个请求返回了 201500,正向测试根本发现不了。

边界用例

把合法输入推到极限。

例子:

  • 空列表
  • 最大字符串长度
  • Unicode 姓名
  • 允许范围内的负数
  • 极大数字
  • 重复值
  • 夏令时切换时间戳边界

一个实操规则:每个正向测试至少配一个负向测试。然后对接受列表、数字、日期或自由文本的输入,再加边界用例。

测试数据和环境

测试靠不靠谱,取决于背后的数据和环境。

用独立的测试数据

别依赖生产数据或某个特定的数据库记录已经存在。

应该这样:

  • 测试执行过程中生成数据
  • 运行前准备好已知状态的 fixture
  • 测试后清理创建的记录
  • 用唯一值避免冲突

让测试互相独立

每个测试自己搞定自己的状态。

避免这种写法:

测试 B 依赖测试 A 创建的 ID

推荐这种:

场景:
1. 创建用户
2. 捕获 userId
3. 通过 userId 查询用户
4. 断言响应
5. 删除用户

在同一个场景内部传值没问题。依赖全局执行顺序就不行。

隔离环境

这些环境要分开:

  • 本地开发
  • CI
  • 预发(Staging)
  • 生产

环境相关的值单独管理:

  • Base URL
  • Token
  • 用户凭证
  • 特性开关
  • 租户 ID

同一套测试,换个环境跑,只需要换变量值,不用改测试本身。

用变量做参数化

别硬编码地址或密钥。

用变量,比如:

{{baseUrl}}
{{accessToken}}
{{tenantId}}
{{userId}}

这样测试可以在本地、CI、预发环境之间自由切换。

左移:测得更早,而不只是测得更多

左移(Shift Left)就是把 API 测试往交付周期更早的阶段挪。

设计阶段发现 Schema 对不上,改几分钟就修好了。同一问题到了生产,可能就变成一次故障。

先设计契约

在动手实现之前先定义好请求响应 Schema。

好处:

  • 提前 review API 形态
  • 生成 Mock
  • 从契约直接创建测试用例
  • 后端还没完成就能让消费方开始验证

用 Mock 测试

前端和集成开发不必总等着后端完工。

从 API 契约生成的 Mock 服务器,可以让消费方早早按预期接口构建和测试。

每次提交都跑快速检查

跑得快的功能测试和契约测试,应该进入开发反馈循环。

在这些时机跑:

  • Pull Request
  • 推送到共享分支
  • 合并之前

在 CI 里自动化

测试策略再完善,不自动化跑也是白搭。

一个实用的 CI 配置分三层。

1. 每次 push

跑快速检查:

  • 功能测试
  • 契约测试
  • 轻量回归测试

断言失败就打断构建。

2. 每日夜间或发布前

跑慢速套件:

  • 集成测试
  • 端到端工作流
  • 负载测试
  • 安全测试

3. 始终坚持发报告

用机器可读的格式输出,比如 JUnit XML,这样 CI 系统可以展示:

  • 通过数
  • 失败数
  • 失败测试名称
  • 趋势变化

一个最简的 GitHub Actions 例子:

name: api-tests
on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 - name: Run API tests run: npm test

命令取决于你用的工具,但模式是一样的:

  1. Check out 代码
  2. 准备运行时环境
  3. 跑测试套件
  4. 非零退出码打断构建
  5. 发布报告

测试策略起步清单

用这个清单逐步建立你的 API 测试策略。

  • [ ] 按业务风险和流量给接口排优先级
  • [ ] 高风险接口加正向功能测试
  • [ ] 每个接口至少加一个负向测试
  • [ ] 对接受列表、数字、日期、自由文本的输入加边界用例
  • [ ] 对有其他团队或客户调用的 API 加契约测试
  • [ ] 对跨服务流程加集成测试
  • [ ] 本地、CI、预发、生产环境分开
  • [ ] Base URL、Token、ID 用环境变量管理
  • [ ] 用生成或预设的测试数据
  • [ ] 保持测试互相独立
  • [ ] 每次 push 跑快速测试
  • [ ] 测试失败就打断构建
  • [ ] 定时跑慢速集成、负载、安全套件
  • [ ] 在 CI 里发布 JUnit 报告
  • [ ] API 改动时 review 和更新套件
  • [ ] 下线的接口删除对应测试

不需要第一天就全搞定。先从最高风险接口的功能测试和负向测试开始,放到 CI 里跑通,然后逐步扩大覆盖面。

FAQ

API 测试策略和测试计划有什么区别?

策略是方法论层面:用什么测试类型、在哪层跑、什么时候跑。

测试计划是针对某次发布或某个功能的,具体到测哪些接口、用什么用例、什么数据、通过标准是什么。

策略相对稳定,每次发布都要重新出计划。

金字塔每层应该放多少测试?

没有固定的数字比例。

形状比具体数量重要:

  • 大量快速单接口测试
  • 适量的集成和契约测试
  • 少量端到端工作流测试

如果慢速顶层测试比快速底层检查还多,就重新平衡。

有了功能测试还需要契约测试吗?

如果其他团队、应用或客户在调用你的 API,就需要。

功能测试验证接口行为,契约测试验证接口形态没有被改成破坏调用方的样子。

一个接口可能"能用",但改了字段名、类型或 Schema,照样把依赖它的客户端全部搞挂。

负载测试应该多久跑一次?

这些时机要跑:

  • 上线前
  • 预期流量高峰前
  • 每周或每月跑一次防止性能退化

别在每次提交时跑重量级负载测试。快的测试放 CI,负载测试单独跑。

整个策略都能在 CI 里自动化吗?

可重复的部分可以自动化。

功能测试、集成测试、契约测试、回归测试通常在 CI 里跑得很好,可以作为合并门槛。

负载测试和安全测试往往定时跑或者跑在专用基础设施上,因为它们更慢或者资源消耗更大。