Skip to content

HTTP 接口测试设计 ​

T2-001必学进阶约 90 分钟JDK 21测试专项 B:接口·UI·性能 · 接口自动化

1. 本节定位 ​

单元测试(T1-001 ~ T1-012)验证的是一个类;接口测试验证的是一个契约:

text
单元测试:  给方法这些参数 → 返回这个值
接口测试:  发这个请求(方法/路径/头/体)→ 应该得到这个状态码、这个结构、这些业务结果

本节不讲工具(《REST Assured 与 Java HTTP Client》),先讲"设计"——因为"测什么"永远比"怎么发请求"重要 ✓

2. 学习目标 ​

  • 说出接口测试的四个层次(契约 / 功能 / 异常 / 安全)★;
  • 说出四类断言(状态码 / 结构 / 业务字段 / 错误契约)★★;
  • 用等价类与边界值方法为一个接口设计完整用例矩阵 ★★★;
  • 说出正向与反向用例的比例原则;
  • 说出测试数据的三种来源与取舍;
  • 能划清接口测试与单元测试的边界(避免重复或遗漏)★★。

3. 前置知识检查 ​

4. 为什么需要这个知识 ​

先看"想到哪测到哪"的典型结果:

text
测试了:正常创建、正常查询、正常删除          ← 都过了 ✓
上线后:
  · 有人传 quantity=0 的订单 → 创建成功 → 负数库存 ✗
  · 有人用 GET 删除接口 → 405 却没有提示语义 ✗
  · 重复点击"提交" → 生成了两笔订单 ✗✗

问题不在"没测",在"没有系统的设计" ✗ ——接口测试是"设计出来的",不是"写出来的" ✓

5. 核心概念 ​

5.1 接口测试的四个层次 ​

层次验证什么例子
① 契约层 ★状态码、结构、头与文档一致201 必须带 Location ✓
② 功能层 ★业务行为正确满 2 件打 9 折 → 金额对 ✓
③ 异常层 ★★非法输入被正确拒绝quantity=0 → 422 + 逐字段错误 ✓
④ 安全层 ★★鉴权、越权、注入被挡住未登录 → 401;普通用户删单 → 403(《Spring Security 认证与授权》)✓

比例参考:①② 约 30%,③ 约 50%,④ 约 20% ✓ ——异常与安全用例的数量通常是正向的 2~3 倍 ⚠️(新手常见误区是 90% 都是正向 ✗)

5.2 四类断言 ★★ ​

每条用例至少覆盖前两类,关键用例覆盖四类 ✓

断言类型断什么例子
① 状态码 ★语义是否正确创建 201、删除 204、校验失败 422 ✓
② 响应结构 ★字段存在与类型details 是数组且长度 2 ✓
③ 业务字段 ★★具体业务值amountInCents = 39800(不是只查"有值")✓
④ 错误契约 ★★code + traceId + detailscode = VALIDATION_FAILED ✓(《参数校验与全局异常处理》)

⚠️ 一个常见不足:只断言状态码 ✗

java
then().statusCode(201);        // ✗ 不够——返回什么字段?金额对不对?Location 有吗?

后果:接口改坏了业务逻辑(如金额算错)但状态码仍是 201 → 测试照样绿 ✗✗

5.3 用例设计:等价类与边界值 ★★★ ​

以本项目的"创建订单"接口为例(《参数校验与全局异常处理》 的 DTO):

java
productId: @NotBlank
quantity:  @Min(1) @Max(100)
address:   @NotBlank @Size(max = 200)

第一步:为每个字段划分等价类与边界

字段有效等价类无效等价类边界
productId非空字符串null / 空串 / 纯空白—
quantity1~100 的整数0、负数、101、非整数、非数字0 / 1 / 100 / 101 ★
address1~200 字符空 / 201 字符200 / 201 ★
请求体合法 JSON非法 JSON / 空体 / 无 Content-Type—
Content-Typeapplication/jsontext/plain / 缺失—

第二步:组合成用例矩阵 ★★★

#用例请求要点期望类别
1正常创建三字段合法201 + Location + 字段回显正向
2quantity 下边界 =1min201 ✓边界
3quantity 上边界 =100max201 ✓边界
4quantity =0min-1422 + quantity 错误边界 ✗
5quantity =101max+1422边界 ✗
6quantity =-5负数422反向
7quantity =1.5非整数400(类型转换,《Spring MVC 请求映射与参数绑定》)✓反向
8quantity ="abc"非数字400反向
9productId 空串@NotBlank422反向
10productId 纯空白" "422(@NotBlank 的意义)✓反向
11address 201 字符max+1422边界 ✗
12address 200 字符max201 ✓边界
13三个字段都非法一次性全错422 + details=3 条 ★反向组合
14请求体非法 JSON{"x400(《JSON 与 XML》 的 HttpMessageNotReadableException)反向
15缺 Content-Type默认表单415 ✗反向
16空 JSON {}全缺422 + 每字段一条错误 ★反向
17未认证无 token401(《Spring Security 认证与授权》)安全
18重复提交两次相同请求201 + 201(幂等性?→ T2-007)⚠️特殊

★ 这张矩阵就是 T2-001 的核心产出 ✓ ——18 条用例覆盖了 5.1 的全部四层 ✓

用例 13 是新手最容易漏的:"一次提交多个错误"——它验证的是错误收集机制(《参数校验与全局异常处理》 的"Bean Validation 收集全部错误")✓

5.4 正向与反向的比例 ​

经验值:反向 : 正向 ≈ 2:1 ~ 3:1 ✓

为什么反向更多说明
每个正向输入对应 N 个非法变体一个 int 字段有:空、非数字、超界、溢出……✓
正向路径通常被" happy path"覆盖而异常路径只有刻意设计才会覆盖 ⚠️
线上事故多来自异常路径 ✗用户永远会输入你没想到的东西 ✓

⚠️ 但也要防"为反向而反向":

"quantity=999999999999" 与 "quantity=101" 是同一个等价类(超上界) ——不需要都测 ✓

同一等价类取一个代表即可 ✓(等价类方法的全部意义就在于此 ✓)

5.5 测试数据的三种来源 ★ ​

来源做法优点缺点
① 用例内构造 ★测试里自己建数据(如 《Vue 基础与组件开发》 的建单→查询)✓自包含、可重复 ✓✓代码量大
② 共享测试数据准备一批"公共数据"快互相污染 ✗(T1-017 的教训)
③ 随机生成每次随机覆盖广难复现失败 ✗(要配合 seed 记录)

★ 推荐组合:① 为主——每个测试"自己创建、自己断言、自己清理" ✓

这与 T1-017 的"测试数据隔离"是同一条原则 ✓

5.6 接口测试与单元测试的边界 ★★ ​

问题单元测试接口测试
金额计算的五种分支✅ 单元测试(毫秒级)✓❌ 不用(T1-017 6.3)✗
接口的参数校验规则❌(校验由框架做)✅ 切片/接口测试(T1-016)✓
跨层协作(Controller→Service→存储)❌✅ 接口测试 ✓
业务规则(如"商品是否存在")✅ Service 单元测试(mock 数据层)✓⚠️ 集成测试抽 1~2 条即可

判断标准(与 T1-017 的"该写集成测试吗"一致):

"这个验证需要真实的 HTTP 与完整的链路吗?"

  • 需要(路由、绑定、校验、序列化、跨层)→ 接口测试 ✓
  • 不需要(纯计算、纯分支)→ 单元测试 ✓

接口测试的数量级 = 接口数 × 关键场景(不是"代码分支数")✓

6. 递进示例 ​

6.1 最小示例:一条合格的用例 ​

java
given().contentType(JSON).body(合法请求)
  .when().post("/api/orders")
  .then().statusCode(201)
        .header("Location", containsString("/api/orders/"))      // ② 结构
        .body("orderNo", startsWith("ORD-"))                     // ③ 业务
        .body("status", equalTo("CREATED"));                     // ③ 业务

6.2 业务示例:完整用例矩阵 ​

见 5.3 的 18 条 ✓

6.3 反例:只测正向 ​

java
// ✗ "测试完成"——但异常路径 0% 覆盖
@Test void testCreate() { ...正常创建... }

7. 用例矩阵(完整版) ​

见 5.3 的 18 条用例表 ✓ ——它是本节的核心交付物 ✓

落地方式:矩阵可以直接变成参数化测试(T1-002 的 @MethodSource)✓

java
static Stream<Arguments> invalidRequests() {
    return Stream.of(
        Arguments.of("quantity=0",  """
            {"productId":"P","quantity":0,"address":"上海"}""", "quantity", "数量必须大于 0"),
        Arguments.of("productId 空白", """
            {"productId":"   ","quantity":1,"address":"上海"}""", "productId", "商品编号不能为空")
        // ……与矩阵一一对应 ✓
    );
}

★ "设计矩阵 → 参数化测试"就是从设计到代码的最短路径 ✓

8. Debug 观察点 ​

  1. 对照 《Spring MVC 请求映射与参数绑定》 的九场景:用矩阵逐条核对——有多少条已被覆盖、多少条漏了 ⚠️;
  2. 数一数"反向用例"的比例:如果不到 50%,说明设计不完整 ⚠️;
  3. 找"组合用例":"多个字段同时非法"有没有覆盖(用例 13)★。

9. 常见错误 ​

#错误说明
1只测正向 ✗异常路径才是事故高发区(5.1)✗
2只断言状态码 ✗业务算错但状态码对 → 测试照样绿 ✗✗(5.2)
3不测组合(多字段同时非法)错误收集机制没有验证 ⚠️
4测试数据互相依赖 ✗顺序化测试 = 脆弱(T1-007 的 I 原则)✗
5忘了幂等/重复提交类用例"重复点击提交"是真实场景 ★(T2-007)
6把单元测试的用例搬到接口层重复且慢(5.6)✗

10. 动手实验 ​

目标:亲手为"订单接口"完成 18 条用例矩阵的设计与实现。

步骤:

  1. 按 5.3 完成矩阵设计(先不看本文给的表,自己设计,再对照——设计差异就是你的盲区)★★;
  2. 把矩阵实现成参数化测试(T2-005 的 REST Assured + @MethodSource)✓;
  3. 跑一遍,确认 18 条的状态码与断言全部符合预期 ✓;
  4. 故意破坏后端:把 @Min(1) 删掉 → 观察哪些用例变红 ★ ——这就是"测试在保护你"的现场 ✓;
  5. 统计:18 条里正向几条、反向几条、边界几条(对照 5.4 的比例)✓。

验收方式:能说出第 4 步"删掉 @Min 后哪条用例红了、它断言的是什么"。

扩展任务:为"查询订单列表"接口(分页 + 状态筛选)设计用例矩阵:

  • 维度:page(0/1/负数/超大)、size(1/上限/0/负数)、status(合法/非法/缺省)、组合;
  • 注意"无匹配结果"的期望(空数组 + 200,而不是 404——《Spring MVC 请求映射与参数绑定》 的 6.2)✓
  • 输出用例矩阵并实现 ✓。

11. 练习题 ​

基础题

  1. 判断:接口测试主要是"把每个接口的正向流程跑一遍"。(对 / 错)
  2. 填空:四类断言是 、、、;每条用例至少覆盖前 ______ 类。
  3. 简答:为什么"异常与安全用例通常比正向用例多"?

代码阅读题

java
public record CreateUserRequest(
    @NotBlank @Size(min = 4, max = 20) String username,
    @NotBlank @Size(min = 8, max = 32) @Pattern(regexp = ".*\\d.*") String password,
    @Email String email
) { }
  1. 为 password 字段设计全部边界与无效等价类用例(列出请求与期望)。
  2. 哪两条规则可以合并成一条参数化用例?为什么?

编程题

  1. 为"文章"接口设计完整用例矩阵(《参数校验与全局异常处理》 综合任务的接口):
    • 接口:POST /api/articles(创建,标题唯一→409);
    • 要求:至少 15 条用例,覆盖四层,含"标题重复 → 409"与"全字段非法 → 多条 details";
    • 实现为参数化测试并跑通;
    • 回答:哪条用例是你"设计时差点漏掉"的?为什么它重要?

综合任务

  1. 为团队制定"接口测试用例设计规范":
    • 背景:团队的接口测试平均每个接口只有 2 条用例,线上故障频出;
    • 要求:
      • 设计用例设计检查清单(review 接口测试时逐条对照,至少 8 条)★;
      • 说明如何度量"用例设计质量"(用例数?缺陷发现率?异常覆盖比例?);
      • 设计**"接口测试先行"的流程**(接口契约定了就先写测试?还是实现后再补?)★;
      • 说明:为什么"用例矩阵"应该作为接口文档的一部分(《接口文档与前后端联调》)?
    • 额外要求:说明这套设计与 T1-005 的"测试左移"是什么关系。
分级提示

提示 1(方向):第 1 题的方向是"正向只是很小的一部分"。

提示 2(关键点):第 5 题——"min 8 位"与"必须含数字"是两个独立的规则,各有各的边界。

提示 3(接近答案):第 7 题"接口测试先行"——契约(《接口文档与前后端联调》)定了,用例矩阵就能先于实现完成 ✓ 这正是"测试左移"的落地形态。

12. 参考答案(要点) ​

基础题

  1. 错。正向只是设计起点——异常与安全层通常占一半以上 ✓(5.4)
  2. 状态码、响应结构、业务字段、错误契约;二 ✓
  3. 因为"用户会输入你没想到的东西",而异常路径只有刻意设计才会覆盖 ✓——线上事故多来自异常路径 ✗

代码阅读题

  1. password 的用例:
用例请求值期望
下边界 =8 位"12345678"通过(若含数字 ✓)
上边界 =32 位32 字符含数字通过 ✓
7 位"1234567"422:长度 8~32 ✗
33 位33 字符422 ✗
8 位但无数字"abcdefgh"422:必须含数字 ✗
空串""422:NotBlank ✗
null缺字段422 ✗
纯空白" "422 ✗

★ 注意"两个条件的组合":"8 位含数字"通过、"8 位无数字"失败、"7 位含数字"失败 ——多条规则叠加时,组合用例最容易漏 ⚠️

  1. 可以合并:"7 位无数字"与"33 位无数字"是同一规则(长度)的两个边界——可参数化为一条 ✓ 但"8 位无数字"不能合并进去——它是另一条规则(@Pattern) ✓

编程题

  1. 要点提示:至少包含——正常创建、标题 1 字符/最大/超长、内容为空、标题重复(两次创建 → 409)、全字段非法(details 多条)、未认证 401、非法 JSON 400。"标题重复"需要两条用例配合(先创建一次,再用同名创建第二次)——这就是"接口依赖"(T2-007)✓

综合任务

  1. 核心要点:
项内容
检查清单① 有没有等价类表 ② 边界两侧都测了吗 ③ 组合非法用例 ④ 错误契约断言 ⑤ 幂等/重复 ⑥ 鉴权 ⑦ 数据自包含 ⑧ 反向比例 ≥ 正向
质量度量"每千行代码发现的接口级缺陷数" 与 "异常路径用例占比" ✓
测试先行契约(OpenAPI)→ 用例矩阵 → 实现 ——测试先红后绿 ✓(T1-006 的 TDD 在接口层的形态)
矩阵进文档矩阵是"契约的可执行形式" ——它比"描述性文档"更不容易过期 ✓(《接口文档与前后端联调》)
与测试左移的关系测试左移 = 把测试活动提前到设计与开发阶段——"接口测试先行"就是它在接口层的具体形态 ✓
:::

13. 掌握标准 ​

  • [ ] 能说出接口测试的四个层次与大致比例;
  • [ ] 能说出四类断言,并知道"只断言状态码"是不够的 ★★;
  • [ ] 能为一个接口产出完整的用例矩阵(等价类 + 边界 + 组合)★★★;
  • [ ] 知道反向用例应多于正向;
  • [ ] 能划清接口测试与单元测试的边界 ★★;
  • [ ] 知道"测试数据自包含"的原则(T1-017)✓。

14. 扩展资源 ​

15. 下一步学习 ​

《REST Assured 与 Java HTTP Client》:把这些用例矩阵变成自动化代码。

进度只保存在本机浏览器,可随时清除

个人学习项目:AI 生成内容均经人工复核,第三方资源仅作索引