Skip to content

接口测试报告与失败分析 ​

T2-008建议学进阶约 45 分钟JDK 21测试专项 B:接口·UI·性能 · 接口自动化

1. 本节定位 ​

测试的价值不在"跑过",而在**"结果被正确解读、失败被正确处理"** ✓

三件小事决定测试体系的生死:

事做对做错
报告一眼看出"什么坏了、坏在哪" ✓数字一堆,没人看 ✗
失败分类产品 bug / 测试 bug / 环境——各修各的 ✓全部"再跑一遍" ✗
flaky 处理修到稳定 ✓✓"重跑就绿"成为习惯 → 测试失效 ✗✗✗

2. 学习目标 ​

  • 能解读 surefire 报告(T1-014 体系的延续)与两种失败(断言失败 / 异常错误)★;
  • 掌握失败分类学(五类,各自的处理路径)★★★;
  • 会做失败分析(复现 → 分类 → 定位 → 修复 → 防复发)★★;
  • 能识别并处理 flaky test ★★★;
  • 知道报告的"受众思维"(给谁看、看什么)★★。

3. 前置知识检查 ​

  • 会跑接口测试并看过 surefire 报告吗?(T2-005、T1-014)
  • 知道 T1-007 的 FIRST 原则吗?(R = Repeatable 与 flaky 直接相关 ✓)

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

一个真实的滑坡过程:

text
第 1 周:CI 有 1 条不稳定测试 → "重跑一次就绿了,没事"
第 1 月:不稳定测试累积到 5 条 → "CI 红了先重跑再说" ✗
第 2 月:真 bug 出现 → CI 红 → 大家习惯性重跑 → "哦,还是红的" → 忽略 ✗✗
第 3 月:事故发生 → 复盘发现"CI 其实早就红了,只是没人看" ✗✗✗

根因:第一条 flaky 没有被当回事 ✗

测试体系的价值 = 单个测试的可信度 × 团队对结果的信任 ✓

flaky 是在同时摧毁这两个因子 ✗✗✗

5. 核心概念 ​

5.1 报告的三个层次 ​

层次内容受众
单条测试哪个用例、断言什么、为什么失败写测试的人 ✓
批次报告通过率、耗时分布、失败分布(按模块/类型)团队(每日站会)✓
趋势缺陷发现数、flaky 数量、覆盖变化管理者(T1-018)⚠️

surefire 报告的解读(回顾 T1-014):

text
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 21.85 s

两种失败要分开 ★:

类型在报告里含义
Failures断言失败代码跑了,结果不符合期望 ★
Errors抛了异常代码根本没跑到断言(NPE、连接失败)⚠️

排查方向完全不同:

text
Failures  → 先看"期望 vs 实际"——是断言错了还是实现错了?
Errors    → 先看异常堆栈——通常在到达断言之前就炸了(环境/数据/NPE)✓

5.2 失败分类学 ★★★ ​

失败的五种分类(每一类有自己的处理路径):

分类特征谁修修什么
① 产品 bug ★断言失败,但测试的期望是对的开发修产品 + 加回归说明 ✓
② 测试 bug测试本身写错(路径、参数、断言)测试作者修测试 ✓
③ 环境本地过、CI 挂(或全员挂)运维/开发恢复环境 ✓
④ 数据过期依赖的预置数据变了测试作者改自建自清(T2-006)✓
⑤ flaky(不稳定)同一代码时红时绿 ✗✗测试作者隔离变量并修根因 ★★★

分类的第一个动作:

"这条失败,在别人机器上、在 CI 上、重跑一次,还失败吗?" ★

答案分类
全都失败① 或 ②(逻辑问题)✓
只有 CI 失败③(环境差异)✓
有时失败⑤(flaky)⚠️
曾经过、现在挂④ 或 ①(什么变了?)★

5.3 失败分析的流程 ★★ ​

text
① 复现 ★
   在本地跑一次失败的用例(单独跑,不带其他)
   → 能复现?→ 走逻辑分析
   → 不能?  → 环境或 flaky(③⑤)

② 分类(5.2 的五选一)

③ 定位
   Failures → 看"期望 vs 实际"的具体差异
   Errors   → 看异常堆栈的最上层(Caused by)★

④ 修复
   ① → 修产品代码 + 补回归说明 ✓
   ② → 修测试
   ③/④ → 修环境/数据(并把"依赖"写进测试注释 ✓)

⑤ 防复发 ★★★
   这次为什么没拦住?
   · 校验规则缺了 → 补规则(S3-032)
   · 用例没覆盖 → 补用例([T2-001](./T2-001) 的矩阵)
   · 人工步骤忘了 → 自动化(S3-035)

★ 第 ⑤ 步是"测试的测试":每一次失败都应该让体系更强一点 ✓

这与 《Spring Boot 配置管理与多环境》 综合任务的"把事故变成检查"完全一致 ✓

5.4 flaky test ★★★ ​

定义:相同代码、相同环境,结果时红时绿 ✗

常见根因与修法:

#根因修法
1测试顺序依赖 ✗每个测试自建数据(T2-006)✓
2并发/时序(sleep、异步)用"重试+超时"的等待(如 Awaitility 思路)✓ 而不是固定 sleep ✗
3依赖外部系统 ✗mock / Testcontainers(T1-017)✓
4共享时间/随机数注入时钟、记录 seed ✓
5资源冲突(同端口、同名文件)随机化(T2-006 的 UUID 后缀)✓

处理策略:

策略说明
立即修 ★首选 ✓
隔离区(quarantine) ⚠️暂时移出主 CI,单独跑 + 限期修复 ✓
"重跑 N 次取绿" ✗✗✗这不是修复,是掩盖 ✗ ——它会杀死整个测试体系 ✗✗✗

★ "重跑取绿"为什么致命:

它会训练出"CI 红了就重跑"的肌肉记忆 ——真正的 bug 也会被"重跑掉" ✗✗

信任一旦丢失,测试体系就死了 ✗✗✗

宁可暂时移出 flaky(并挂牌限期修),也不要让它污染主 CI ✓

5.5 "失败要可复现" ★ ​

最小复现 = 定位的一半 ✓

手段说明
单独跑失败用例mvn test -Dtest=Xxx#method(排除其他测试的干扰)✓
固定随机打印并复用 seed ✓
缩小数据造最小的复现数据(而不是复用整库)✓
记录环境端口、依赖版本、时间 ✓

一个实用技巧:失败时让测试输出"足够的上下文" ——请求体、响应体、traceId(《日志框架与日志规范》)✓ ——否则"下次就复现不了" ✗

5.6 报告的受众思维 ★★ ​

受众他们关心报告要给什么
开发哪个用例挂了、为什么失败详情 + 上下文 + 复现命令 ✓
测试哪些场景没覆盖覆盖与用例矩阵的差距 ✓
团队"现在能不能发"通过率 + 失败清单 ✓
管理者质量趋势趋势图 + 关键指标(T1-018)✓

一条实用规则:

失败的通知里,第一行就要让人知道"要不要现在处理" ✓

例:"主流程用例失败(影响发布)"vs"边缘用例失败(可次日处理)"✓ ——这与 《日志框架与日志规范》 的日志级别判断一致 ✓

6. 递进示例 ​

6.1 最小示例 ​

text
Failures: 1
  Expected: 422   Actual: 500
  → 500 说明后端异常(而不是校验失败)——先看后端日志的堆栈 ✓

6.2 业务示例:一次失败的完整分析 ​

text
① CI 红:OrderApiRestAssuredTest.create_should_201 失败
② 复现:本地单跑 → 通过 ⚠️ → 分类:flaky 嫌疑(⑤)
③ 对照 5.4 的根因表:最近改动了"并发创建订单" → **本地是单线程,CI 是并行** ✓
④ 修:测试数据加 UUID 后缀([T2-006](./T2-006))✓
⑤ 防复发:把这个坑写进团队的"接口测试规范"([T2-001](./T2-001) 综合任务)✓✓

6.3 反例:失败信息只有 "expected true but was false" ​

问题:不知道断言的是什么 ✗ ——排查要人肉翻代码 ✗

修:断言要带描述:

java
assertThat(actual).as("订单 %s 的金额应按 9 折计算", orderNo).isEqualTo(expected);

★ JUnit 5 / AssertJ 都支持"描述" ——失败报告立刻可读 ✓

7. 实测与工具 ​

本项目的报告(target/surefire-reports/):

text
cn.kb.learn.web.OrderApiRestAssuredTest.txt
cn.kb.learn.web.OrderQueryIntegrationTest.txt
...

内容(成功时):

text
Test set: cn.kb.learn.web.OrderApiRestAssuredTest
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 21.85 s

失败时的报告会包含:

text
java.lang.AssertionError:
  Expecting...
  but was...
      at cn.kb.learn.web.XxxTest.method(XxxTest.java:42)     ← 精确到行号 ✓

★ 记住这个路径:失败时,target/surefire-reports/ 里有比控制台更完整的信息 ✓(控制台可能被截断,文件里不会)✓

进阶工具:

工具价值
Allure美观报告 + 步骤/附件/历史趋势 ✓
JUnit Platform 报告(5.x)内置 XML/HTML ✓

8. Debug 观察点 ​

  1. 对比 Failures 与 Errors:故意造两类失败,观察报告里的区别 ✓;
  2. Time elapsed 的分布:哪个测试最慢?(慢测试是 CI 的债务,T1-017)✓;
  3. 断言加 .as() 描述:故意失败,观察报告可读性的变化 ✓;
  4. surefire 文件 vs 控制台:对比两者信息量 ✓。

9. 常见错误 ​

#错误说明
1"重跑就绿"当手段 ✗✗✗杀死测试体系(5.4)✗✗✗
2断言无描述 ✗失败后排查靠猜 ✗
3只在控制台看结果surefire 文件信息更全 ⚠️
4失败不分类,全员修产品 ⚠️③④⑤类修产品是浪费 ✗
5flaky 无限期搁置隔离区要挂牌限期修 ✓
6只报告"挂了",不说"影响什么"受众无法决策(5.6)✗

10. 动手实验 ​

目标:亲手经历"失败 → 分类 → 修复 → 防复发"。

步骤:

  1. 故意制造断言失败:把一条断言改错 → 跑 → 读报告(Failures)✓;
  2. 故意制造异常:临时 throw new RuntimeException → 跑 → 读报告(Errors)✓ ——对比两者的报告差异 ★;
  3. 给断言加 .as() 描述 → 再失败 → 观察可读性 ✓;
  4. 造一个 flaky:写一个"断言 System.currentTimeMillis() % 2 == 0"的测试 → 多跑几次 → 体验时红时绿 ✗ ——然后想"怎么修"(注入时钟 ✓);
  5. 把一次失败写成"分析文档":复现步骤、分类、根因、修复、防复发(这就是"测试的复盘")✓。

扩展任务:设计一个"flaky 治理流程":

  • 定义:连续 3 次失败且"重跑能过"→ 标记 flaky;
  • 隔离区:标记后移出主 CI、单独跑 + 7 天内必须修;
  • 度量:flaky 数量趋势(T1-018 的"指标思维")✓;
  • 说明:为什么"隔离"比"容忍"好、比"删除"好 ⚠️。

11. 练习题 ​

  1. 判断:Failures: 1 说明代码抛了异常。(对 / 错)
  2. 填空:失败五分类是 、、、、______。
  3. 简答:为什么"flaky 测试"比"稳定失败的测试"更危险?
  4. (代码阅读)assertThat(x).isEqualTo(y) 失败报告信息有限,怎么增强?
  5. (编程)写一个"注入时钟"的例子:测试时间相关逻辑时用可注入的 Clock 而不是 LocalDateTime.now()——消除时序 flaky ✓。
参考答案(要点)
  1. 错。Failures = 断言失败(代码跑到了断言);Errors = 抛异常(没跑到断言) ✓(5.1)★
  2. 产品 bug、测试 bug、环境、数据过期、flaky ✓
  3. 因为"不稳定"摧毁信任 ✗✗:
    • 稳定失败 → 大家立刻知道"坏了",会修 ✓
    • flaky → 大家学会"重跑" → 真 bug 被重跑掩盖 ✗✗✗
    • 且 flaky 让 CI 的"红灯"失去信息量(不知道是不是真问题)✗

一个类比:狼来了的故事 ——警报响得太多,狼真来了没人信 ✗✗✗

  1. 加描述:.as("上下文说明") / 参数化 name / 失败时打印请求与响应(log().ifValidationFails(),T2-005)✓
  2. 要点:定义 Clock 接口注入(生产用 Clock.systemDefaultZone(),测试用固定的 Clock.fixed(...))✓ 原理:把"不确定"变成"确定" ——flaky 的根因常常就是"不确定"(时间、随机、并发、外部)✓

12. 掌握标准 ​

  • [ ] 能区分 Failures 与 Errors 并采取不同排查方向 ★;
  • [ ] 掌握失败五分类与各自的处理路径 ★★★;
  • [ ] 会走"复现 → 分类 → 定位 → 修复 → 防复发"流程 ★★;
  • [ ] 能识别 flaky 并知道处理策略(修 / 隔离 / 绝不容忍重跑)★★★;
  • [ ] 断言失败信息"可定位、可理解"(.as() + 上下文)✓;
  • [ ] 知道报告要按"受众"提供信息 ★。

13. 扩展资源 ​

14. 下一步学习 ​

接口测试五节(T2-001/005/006/007/008)完成。TB 的后续:

后续知识

暂无

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

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