Skip to content

测试环境与数据治理 ​

T2-018建议学高级约 45 分钟JDK 21测试专项 B:接口·UI·性能 · 测试流水线

1. 本节定位 ​

《CI 分层执行与报告归档》解决了"结果可追溯";本节解决**"为什么这次跑得通、下次跑不通"**:

text
本地跑通 → CI 挂了 → 看日志 → "Connection refused"      ← 环境问题 ✗
第一次跑通 → 第二次挂了 → 数据重复了                    ← 数据问题 ✗

本节的两个主题:环境(跑到哪去)与数据(用什么数据、用完怎么还)✓

2. 学习目标 ​

  • 说出环境的三个层次与各自的用途 ★;
  • 说出"环境一致性"的四个抓手(版本锁定 / 容器 / 配置外置 / 依赖隔离)★★★;
  • 说出测试数据的四种准备方式与各自代价 ★★★;
  • 说出数据隔离的三种策略(独立库 / Schema / 事务回滚)★★;
  • 知道"用完必须清理"的两条理由(幂等 + 不污染别人的数据)★★★;
  • 知道"环境不可用就跳过"的两难,以及怎么让它不被滥用 ★★★。

3. 前置知识检查 ​

  • 知道 Maven 的依赖与 profile 吗?(《Maven 基础》)✓
  • 做过"自建自清"的接口测试吗?(T2-006)★★

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

"在我机器上是好的"这句话背后,通常是四个不同的问题 ✗:

说法真实原因属于
"我这里是好的"CI 的 JDK 版本不同环境一致性 ✗
"我这里是好的"本地有上次留下的数据,CI 是干净的数据准备 ✗
"我这里是好的"本地跑第 2 次就不行了(数据重复)幂等性 ✗✗
"我这里是好的"本地连的是测试库,CI 连的是另一个配置外置 ✗

这四类问题的共同点:它们都不是"代码问题",但都表现为"测试失败" ✓

所以它们必须被单独治理 ✓✓

5. 核心概念 ​

5.1 环境的三个层次 ★ ​

环境用途数据谁在用
本地开发自测随便造开发者 ✓
CI ★★每次提交验证必须隔离、必须可重复流水线 ✓
预发/测试环境 ★集成、联调、UI、性能接近生产但可污染测试团队 ✓

★ 关键认识:

同一套测试代码,在不同环境上的"前置条件"可能完全不同 ✓

比如本项目的 UI 测试:本地要手工把应用起起来;CI 里应该由流水线负责启动 ✓

所以"测试代码"要能表达"我需要什么前置条件",而不是假设它天然存在 ✓✓

5.2 环境一致性的四个抓手 ★★★ ​

抓手做什么不做的后果
① 版本锁定 ★★★JDK / Maven / 依赖版本写死"昨天还好好的" ✗
② 容器化 ★★用镜像固定 OS 与系统依赖换台机器就挂 ✗
③ 配置外置 ★★★连接串、端口、开关从环境读连错库 ✗✗
④ 依赖隔离 ★★测试用的 DB/缓存独立于开发环境测试污染别人的数据 ✗✗

★ ① 的实操:

text
✗ 用 mvn(版本随机器)
✓ 用 ./mvnw(Maven Wrapper,版本锁定)★
✓ 用 maven-enforcer-plugin 强制 JDK 版本

本项目就是这么做的(《Maven 基础》):labs 用 mvnw ✓

★ ③ 的实操:

yaml
# ✗ 写死
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test_db

# ✓ 从环境读,带默认值
spring:
  datasource:
    url: ${DB_URL:jdbc:h2:mem:test}

★ ④ 的一句话原则:

测试可以"脏",但只能脏它自己的那一块 ✓✓

5.3 测试数据的四种准备方式 ★★★ ​

这张表决定了"测试能不能重复跑":

方式做法优点代价
① 硬编码 ✗写死"商品 ID = 1"简单依赖既有数据,脆弱 ✗✗
② 数据工厂 ★★代码造对象(Builder)可控、可读需要维护工厂 ✓
③ API 自建 ★★★用被测接口自己造数据贴近真实、自带清理线索慢一点 ✓
④ 快照/夹具 ★SQL 脚本导入固定数据快、可复现容易与真实数据漂移 ⚠️

★ ③ 是接口测试的首选(T2-006):

java
// 自建:用接口创建一条属于自己的数据
String orderNo = given().body(...).when().post("/api/orders")
    .then().extract().jsonPath().getString("orderNo");

// 自清:用完删掉
given().when().delete("/api/orders/" + orderNo);

它的三个好处:

好处说明
不依赖既有数据 ✓空库里也能跑 ✓
数据形态一定合法 ✓因为是被测系统自己接受的 ✓
能重复跑 ✓每次都新建、每次都删掉 ✓

★ 与 ① 硬编码的对比:

text
✗ 硬编码:断言 "订单 ORD-1 的商品是机械键盘"        ← 别人改了这条数据,测试就挂了
✓ 自建自清:自己造 ORD-x,断言 "它的商品是我填的那个"  ← 谁都不影响 ✓

5.4 数据隔离的三种策略 ★★ ​

策略做法隔离度成本
独立库 ★★★每次跑用一个全新库最强 ✓建库慢 ⚠️
独立 Schema / 表前缀 ★★同一个库里用不同 schema强 ✓需要 DDL 支持
事务回滚 ★测试包在事务里,结束回滚中(不覆盖"跨请求"的场景)最快 ✓
逻辑隔离(唯一 ID)★数据加唯一前缀,用完删弱但够用 ✓最低 ✓

★ 本项目的选择:逻辑隔离 ✓✓

text
唯一 productId:P-<UUID 前 8 位>
唯一地址:含时间戳
用完立即删除([T2-006](../api/T2-006) 的自建自清)

为什么不用"事务回滚":

因为接口测试是"跨 HTTP 请求"的 ✗ ——每个请求是独立事务 ✓

所以"把测试包在事务里回滚"这个经典做法,对接口测试不适用 ✓✓(对"直接调 Service 的集成测试"才适用)

★ 这是一条容易被忽略的边界 ✓

5.5 数据治理:清理与隐私 ★★★ ​

"用完清理"的两条理由(缺一不可):

理由说明
① 幂等 ★★★不清理 → 第二次跑就冲突(唯一约束、数量断言)✗
② 不污染 ★★别人跑测试时看到你留下的脏数据 ✗

★ 判据:"这套测试能不能连续跑两遍都通过?" ✓✓

如果能 → 数据治理合格 ✓ 如果不能 → 一定有"没清理"或"依赖了别人的数据" ✗

★ 另一件必须做的事:隐私 ⚠️

风险对策
测试环境用了生产数据 ✗✗脱敏(手机号、身份证、地址)
报告/截图里带真实用户信息 ⚠️T2-017 5.3④ ✓
日志里打了完整请求体 ⚠️《日志框架与日志规范》 的脱敏 ✓

5.6 "环境不可用就跳过"的两难 ★★★ ​

这是一个真实的设计冲突 ✓ ——本项目就遇到了 ✓

背景:UI 测试需要一个运行中的应用 ✗ ——而应用可能没启动 ✗

两种做法:

做法好处坏处
直接失败 ✗不会漏测 ✓"应用没起"和"页面坏了"都显示为红 ✗ ——分不清是环境问题还是缺陷 ✗
跳过(assumeTrue) ★区分"环境未就绪"与"测试失败" ✓可能悄悄跳过而没人发现 ✗✗(T2-017 5.2)

★ 本项目的选择与配套措施:

java
@BeforeAll
static void checkAppReachable() {
    appReachable = UiEnvironmentCheckTestSupport.canConnect(BASE_URL);
    if (!appReachable) {
        System.out.println("[UI 环境自检] 被测应用未启动(" + BASE_URL + "),本类将跳过。");
    }
}

@BeforeEach
void setUp() {
    assumeTrue(appReachable, "被测应用未启动,跳过 UI 测试");
}

配套措施(关键):

措施作用
ui-test.ps1 负责启动应用 ★★把"前置条件"变成脚本的责任,而不是人的记忆 ✓✓
报告里看 tests 数 ★★★跳过 ≠ 通过(T2-017 5.2)✓
跳过时打印原因 ★日志里留一行,方便判断 ✓

★ 一条可以直接用的判断原则:

"跳过"可以用于"外部依赖未就绪",但绝不能用于"怕失败" ✓✓

区别在于:前者你能写出"怎么让它就绪"的脚本;后者你写不出来 ✓

6. 递进示例 ​

6.1 最小示例:自建自清 ​

java
String orderNo = create();          // 自建
try {
    // 测试逻辑
} finally {
    delete(orderNo);                // 自清:放在 finally 里,失败也清
}

★ finally 那一行很重要:测试失败时更要清理 ✓✓ ——否则脏数据会一直累积 ✓

6.2 业务示例:唯一数据 ​

java
String productId = "P-" + UUID.randomUUID().toString().substring(0, 8);

★ 这也是压测参数化的做法(T2-013 5.1)✓ ——同一个思想:不要复用固定数据 ✓

6.3 反例:依赖既有数据的测试 ​

java
assertEquals("机械键盘", getOrder("ORD-1").getProduct());   // ✗ 别人改了这条数据就挂

7. 实测:本项目的三个真实取舍 ​

本节没有新增代码——它总结的是本项目已经做过的三个决定 ✓

决定做法理由
用 H2 而不是真实 MySQL ★data-access 用 H2 的 MySQL 兼容模式无需安装数据库即可复现 ✓ ——换真实库只改 URL 与驱动两处 ✓
逻辑隔离而非事务回滚 ★唯一 ID + 自建自清接口测试跨请求,事务回滚不适用 ✓(5.4)
环境不可用则跳过(带配套) ★★assumeTrue + 脚本负责启动 + 报告看 tests区分"环境未就绪"与"测试失败" ✓

★ 第一条的代价要诚实说明:

H2 与 MySQL 的行为并不完全一致(《执行计划与慢查询定位》 实测过: H2 的优化器把 YEAR() 与隐式类型转换都改写成了索引扫描,而 MySQL 上会失效)✓

所以:功能类测试用 H2 没问题;索引与执行计划类测试必须在真实库上做 ✓✓

这条边界很重要:"用轻量替代品"能解决 80% 的场景,但要清楚剩下 20% 是什么 ✓

8. Debug 观察点 ​

  1. 连续跑两遍同一套测试 → 第二遍失败就说明数据治理有问题 ★★★;
  2. 在测试前后各统计一次数据行数 → 验证"自清"是否真的生效 ✓;
  3. 把数据库连接串改成环境变量,用两个不同的值跑两次 → 验证配置外置 ✓;
  4. 看报告里的 skipped 数 → 确认没有"因为环境问题悄悄跳过" ★★;
  5. 把应用停掉再跑 UI 测试 → 观察是"跳过"还是"失败"(本项目是跳过 ✓)★。

9. 常见错误 ​

#错误说明
1依赖既有数据 ✗✗空库跑不通(5.3①)✗
2不清理 ✗✗第二次跑就冲突(5.5)✗✗
3清理写在测试末尾而不是 finally/@AfterEach ✗失败时不清,脏数据累积 ✗
4硬编码连接串 ✗✗连错库(5.2③)✗
5用生产数据做测试 ✗✗隐私风险(5.5)✗✗
6assumeTrue 被用来"消音" ✗✗测试悄悄消失(5.6)✗✗
7以为"事务回滚"万能 ✗接口测试跨请求,回滚不了 ✓(5.4)

10. 动手实验 ​

目标:让一套测试"连续跑两遍都通过"。

步骤:

  1. 连续跑两遍你写的接口测试,记录第二遍的结果 ★★;
  2. 如果有失败,找出是"数据没清"还是"依赖了别人的数据" ✓;
  3. 把清理挪到 @AfterEach 或 finally,再连跑两遍 ✓;
  4. 把连接串改成环境变量,用两个值各跑一次 ✓;
  5. 检查报告里的 skipped,确保没有"意外跳过" ★。

验收方式:能说出"你的测试为什么能重复跑"。

扩展任务:给测试加"数据自检":

  • 测试开始前检查"库里没有本次要用的唯一 ID" ✓;
  • 结束后检查"已清理干净" ✓;
  • 说明:为什么这比"记得清理"更可靠(提示:它把约定变成了断言)✓

11. 练习题 ​

  1. 判断:测试数据用固定 ID 更稳定。(对 / 错)
  2. 填空:四种测试数据准备方式里,接口测试首选 ______;它的两个关键动作是 ______ 与 ______。
  3. 简答:为什么"事务回滚"这个经典隔离策略不适用于接口测试?
  4. (代码阅读)某测试类用 assumeTrue(env.isReady()) 包裹全部用例,报告显示 tests=30 skipped=30。有什么问题?怎么改?
  5. (编程)写一个"幂等自检"脚本:连续跑两遍指定的测试类,比较两次的 surefire 结果。
参考答案(要点)
  1. 错。固定 ID 意味着"依赖既有数据" ✗ ——别人改了就挂,且无法在空库上跑 ✓ → 应该用唯一 ID + 自建自清 ✓
  2. API 自建 + 用完清理;自建;自清(放在 finally) ✓
  3. 因为接口测试通过 HTTP 请求驱动,每个请求是独立事务 ✓ ——测试代码无法把多个请求包进一个可回滚的事务 ✓★
  4. 30 个用例全部被跳过,而流水线可能是绿的 ✗✗ ——"跳过"变成了"消音" ✓ 改法:① 让脚本负责把环境准备好(不要靠 assumeTrue 兜底)✓;② 如果确实无法就绪,应该让流水线"失败"而不是"跳过" ✓;③ 至少要在报告汇总里把 skipped 显示出来 ✓
  5. 要点:跑两遍 + 比较 tests/failures/skipped ✓ ——第二遍的 failures 应为 0 ✓

12. 掌握标准 ​

  • [ ] 能说出环境一致性的四个抓手 ★★★;
  • [ ] 能说出四种数据准备方式与代价 ★★★;
  • [ ] 知道接口测试首选"API 自建 + 自清",并知道理由 ★★★;
  • [ ] 知道"事务回滚"对接口测试不适用 ★★;
  • [ ] 能判断"这套测试能不能连续跑两遍",并知道怎么修 ★★★;
  • [ ] 知道"跳过"的正当用途与滥用后果 ★★★;
  • [ ] 知道测试数据必须脱敏 ✓。

13. 扩展资源 ​

14. 下一步学习 ​

TB 测试专项 B 到此完成。后续:

后续知识

暂无

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

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