测试体系
1. 这是什么
测试体系用于保障系统在持续修改中的正确性和稳定性。
它不是单一测试工具,而是一套覆盖不同层次的验证策略。
一句话理解:
- 测试不是“为了跑绿”
- 而是给系统演进建立安全网
2. 为什么重要
没有测试支撑,重构和迭代都会变得危险。
测试做得好,系统演进速度和质量通常都更稳。
真正有价值的测试体系可以回答这些问题:
- 核心业务规则还对吗
- 模块协作还对吗
- 改动是否影响旧功能
- 上线前有没有明显回归风险
3. 先建立直觉:不是所有测试都在做同一件事
最常见的误区之一是:
- 觉得“只要有测试就行”
其实不同层次测试解决的问题不同:
| 测试层次 | 更关注什么 |
|---|---|
| 单元测试 | 单个类 / 单个规则是否正确 |
| 集成测试 | 多个组件协作是否正确 |
| 接口测试 | 对外行为是否符合契约 |
| 回归验证 | 改动后旧功能有没有被破坏 |
所以测试体系不是一类测试,而是:
- 多层测试各自承担不同职责
4. 核心内容
4.1 单元测试
单元测试更适合验证:
- 纯业务规则
- 计算逻辑
- 状态转换
特点:
- 速度快
- 定位细
- 更适合频繁运行
4.2 集成测试
集成测试更适合验证:
- 组件之间是否正确协作
- 配置是否正确
- 数据访问链路是否正确
它比单元测试更接近真实运行环境,但成本也更高。
4.3 接口测试
接口测试更适合验证:
- API 契约
- 输入输出格式
- 状态码和错误码
它更偏“对外行为是否符合预期”。
4.4 测试数据准备
很多测试之所以难维护,不是因为断言写得难,而是:
- 测试数据准备混乱
一个好的测试数据设计应该尽量做到:
- 可读
- 最小化
- 可复用
- 不互相污染
4.5 回归验证
回归验证的价值在于:
- 旧功能在新改动后仍然正确
这也是为什么测试体系和持续交付关系很大。
没有回归验证,系统越改越不敢发版。
5. 学习重点
这一章最重要的是掌握:
- 不同测试层次的职责边界
- 测试不是追求数量,而是追求有效覆盖
- 单元测试更偏规则验证,集成测试更偏协作验证
- 测试是重构和持续交付的安全网
6. 常见问题
6.1 只关注覆盖率,不关注测试价值
覆盖率是参考,不是目标本身。
无价值的测试即使很多,也挡不住真实 bug。
6.2 单元测试写得过于依赖实现细节
这样一重构就全碎,维护成本会很高。
6.3 缺少集成测试导致环境问题频发
只做单元测试,不代表组件协作就一定正确。
7. 动手验证
这一节我用纯 Java 做一个 mini test harness,帮助你直观看到“单元测试”和“集成测试”在关注点上的差异。
7.1 准备一个可运行示例
新建文件 TestingSystemDemo.java,内容如下:
java
public class TestingSystemDemo {
interface DiscountRepository {
int findDiscountByUser(long userId);
}
static class FakeDiscountRepository implements DiscountRepository {
@Override
public int findDiscountByUser(long userId) {
return userId == 1L ? 20 : 0;
}
}
static class PriceService {
private final DiscountRepository repository;
PriceService(DiscountRepository repository) {
this.repository = repository;
}
int finalPrice(long userId, int amount) {
return amount - repository.findDiscountByUser(userId);
}
}
private static void assertEquals(String name, Object expected, Object actual) {
if ((expected == null && actual != null) || (expected != null && !expected.equals(actual))) {
throw new RuntimeException(name + " failed, expected=" + expected + ", actual=" + actual);
}
System.out.println(name + "=PASS");
}
public static void main(String[] args) {
// 单元测试:只关注规则
PriceService unitService = new PriceService(userId -> 10);
assertEquals("unitTest-finalPrice", 90, unitService.finalPrice(999L, 100));
// 集成测试:验证服务和仓储的协作
PriceService integrationService = new PriceService(new FakeDiscountRepository());
assertEquals("integrationTest-vipPrice", 80, integrationService.finalPrice(1L, 100));
assertEquals("integrationTest-normalPrice", 100, integrationService.finalPrice(2L, 100));
System.out.println("regressionChecklist=true");
}
}7.2 编译并运行
bash
javac TestingSystemDemo.java
java TestingSystemDemo7.3 你应该观察到什么
输出应包含这些关键信息:
text
unitTest-finalPrice=PASS
integrationTest-vipPrice=PASS
integrationTest-normalPrice=PASS
regressionChecklist=true7.4 每一行在验证什么
unitTest-finalPrice=PASS:说明单元测试更关注单个业务规则是否正确integrationTest-vipPrice=PASS、integrationTest-normalPrice=PASS:说明集成测试更关注组件协作结果是否正确regressionChecklist=true:说明回归验证本质上是在确认已有正确行为没有被破坏
8. 练习建议
下面这些练习做完,这一章会更扎实:
- 为核心业务逻辑写一组单元测试
- 为数据库或接口链路写一个集成测试
- 设计一份回归验证清单
- 复盘一个“覆盖率不低但还是出事故”的测试案例
9. 自测问题
- 单元测试和集成测试的职责差别是什么?
- 为什么测试是系统可持续演进的关键?
- 什么样的测试最容易沦为无效测试?
- 为什么测试数据设计会直接影响测试质量?
10. 自测核对要点
如果你的回答能覆盖下面这些点,说明这一章基本掌握到位了:
- 不同测试层次解决的是不同问题
- 单元测试更偏规则验证,集成测试更偏协作验证
- 覆盖率不是唯一目标,有效覆盖更重要
- 测试体系是重构、发版和回归验证的安全网