
做后端开发的,估计没少被 API 的坑折腾过。字段少了一个、状态码返回错了、高并发下超时了、接口契约改了导致调用方全挂了——这些问题用临时手动测的方式,靠运气能碰上几个,但系统化地测才能真正把它们抓出来。
一套好的 API 测试策略,说白了就是回答三个问题:测什么、谁来测、什么时候跑。把快速检查和慢速套件分开,提交代码时跑轻量级检查,定时或发布前跑完整回归,既能保证覆盖率,又不让每个开发流程都卡在等测试上。
动手写断言之前,先把下面四个问题想清楚。
按业务风险和流量优先级来排,不是按测试难度来排。
举个例子:
别按"这个好测"来排,按"出问题了有多疼"来排。
不是所有测试都得做端到端。
用能抓住 Bug 的最低层来测:
把快的和慢的反馈分开:
只检查个 200 OK 那是糊弄自己。
定义清楚这些:
一个断言集的例子:
GET /api/users/42 Expected:
- status = 200
- body.id = 42
- body.email is a valid email
- body matches User schema
- response time < 500ms
临时测基本就是发个请求、瞄一眼返回、然后下一个。演示的时候这么干没问题,生产环境上就漏成筛子了。
一个检查只存在某人的终端历史记录里,别人根本没法可靠地复现。自动化测试每次跑一模一样。
手动测一般只覆盖开发者预期能跑通的情况:
线上的 bug 往往藏在别的地方:
一个服务 40 个接口、5 个环境,每次发布就是 200 个可能的手工检查点。没人会一个一个去跑。
有策略就能把这些变成可重复的自动化:写一次,到处跑,每次改动都跑一遍。
用测试金字塔来决定每层放多少测试。
/\ / \ 端到端/工作流测试 (少,慢,高价值) /----\ / \ 集成/契约测试 (适量,中等速度) /--------\ / \ 单元/单接口测试 (多,快,便宜) /____________\
每个测试只打一个接口,断言返回。
这类测试用来测:
速度快、代价低、好排查。测试套件里大部分应该放在这层。
这些测试验证多个服务之间对数据格式和行为是否达成一致。
用来测:
比单接口测试慢,但能抓到单接口检查漏掉的 Bug。
这些跑完整的用户旅程,跨多个接口。
例子:
1. 创建订单
2. 支付订单
3. 查询支付状态
4. 验证订单状态变为已支付
这类要少。它们提供高置信度,但跑得慢、维护成本高。
常见的错误是把金字塔倒过来:慢速端到端太多,快速接口测试太少。只要低层能抓到同样的问题,就把覆盖率往下压。
完整的 API 测试策略要组合多种测试类型。
功能测试验证接口是否按规格说明运行。
请求示例:
GET /api/users/42 HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
有用的断言:
200
User Schemaid 等于 42
email 是合法邮箱字符串每个重要接口都应该尽早把功能测试自动化。
集成测试验证你的 API 和真实依赖配合是否正常,这些依赖包括:
功能测试用 Mock 能过,接上真实依赖可能就崩了。集成测试就是补这个窟窿的。
回归测试在每次改动后重新跑已有测试,确认本来能跑通的行为没有被破坏。
通常不需要单独写"回归测试"这一类。现有的功能测试、集成测试、契约测试持续跑起来,就成了回归覆盖。
契约测试验证 API 提供方和消费方对请求响应格式是否达成一致。
它能抓住这类破坏性改动:
如果有其他团队、应用或客户在调用你的 API,契约测试是必须的。
负载测试测量 API 在并发流量下的表现。
关注这些指标:
这些场景要跑负载测试:
这和功能测试是分开的,通常需要专门的工具。
安全测试验证 API 是否会拒绝不安全的或未授权的请求。
覆盖这些:
每个处理敏感数据的接口都需要认证和授权检查。
| 测试类型 | 能抓到的问题 | 运行时机 |
|---|---|---|
| 功能测试 | 状态码、Schema、值错误 | 每次提交 |
| 集成测试 | 服务间流程断裂 | 每次提交或每日 |
| 回归测试 | 新改动破坏了已有功能 | 每次提交和发布前 |
| 契约测试 | 接口破坏性变更 | 提供方每次提交 |
| 负载测试 | 流量下的延迟和故障 | 上线前和定期 |
| 安全测试 | 认证、注入、数据泄露 | 发布前和定期 |
每个重要接口都要覆盖三类情况。
发合法输入,期望成功返回。
例子:
POST /api/users HTTP/1.1
Content-Type: application/json { "email": "dev@example.com", "name": "Dev User"
}
期望:
201
发非法输入,期望干净失败。
例子:
400
401
403
404
400
很多真实的 API Bug 都是在负向测试里发现的。
例子:
POST /api/orders HTTP/1.1
Content-Type: application/json { "customerId": "c_123", "quantity": -5 }
期望:
400
quantity 的校验错误如果这个请求返回了 201 或 500,正向测试根本发现不了。
把合法输入推到极限。
例子:
一个实操规则:每个正向测试至少配一个负向测试。然后对接受列表、数字、日期或自由文本的输入,再加边界用例。
测试靠不靠谱,取决于背后的数据和环境。
别依赖生产数据或某个特定的数据库记录已经存在。
应该这样:
每个测试自己搞定自己的状态。
避免这种写法:
测试 B 依赖测试 A 创建的 ID
推荐这种:
场景:
1. 创建用户
2. 捕获 userId
3. 通过 userId 查询用户
4. 断言响应
5. 删除用户
在同一个场景内部传值没问题。依赖全局执行顺序就不行。
这些环境要分开:
环境相关的值单独管理:
同一套测试,换个环境跑,只需要换变量值,不用改测试本身。
别硬编码地址或密钥。
用变量,比如:
{{baseUrl}}
{{accessToken}}
{{tenantId}}
{{userId}}
这样测试可以在本地、CI、预发环境之间自由切换。
左移(Shift Left)就是把 API 测试往交付周期更早的阶段挪。
设计阶段发现 Schema 对不上,改几分钟就修好了。同一问题到了生产,可能就变成一次故障。
在动手实现之前先定义好请求响应 Schema。
好处:
前端和集成开发不必总等着后端完工。
从 API 契约生成的 Mock 服务器,可以让消费方早早按预期接口构建和测试。
跑得快的功能测试和契约测试,应该进入开发反馈循环。
在这些时机跑:
测试策略再完善,不自动化跑也是白搭。
一个实用的 CI 配置分三层。
跑快速检查:
断言失败就打断构建。
跑慢速套件:
用机器可读的格式输出,比如 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
命令取决于你用的工具,但模式是一样的:
用这个清单逐步建立你的 API 测试策略。
不需要第一天就全搞定。先从最高风险接口的功能测试和负向测试开始,放到 CI 里跑通,然后逐步扩大覆盖面。
策略是方法论层面:用什么测试类型、在哪层跑、什么时候跑。
测试计划是针对某次发布或某个功能的,具体到测哪些接口、用什么用例、什么数据、通过标准是什么。
策略相对稳定,每次发布都要重新出计划。
没有固定的数字比例。
形状比具体数量重要:
如果慢速顶层测试比快速底层检查还多,就重新平衡。
如果其他团队、应用或客户在调用你的 API,就需要。
功能测试验证接口行为,契约测试验证接口形态没有被改成破坏调用方的样子。
一个接口可能"能用",但改了字段名、类型或 Schema,照样把依赖它的客户端全部搞挂。
这些时机要跑:
别在每次提交时跑重量级负载测试。快的测试放 CI,负载测试单独跑。
可重复的部分可以自动化。
功能测试、集成测试、契约测试、回归测试通常在 CI 里跑得很好,可以作为合并门槛。
负载测试和安全测试往往定时跑或者跑在专用基础设施上,因为它们更慢或者资源消耗更大。