# 单元、集成、系统、验收：为什么换一层测，就查不到那个 bug

讲清四个测试层级各自让什么变成真的、因此各能抓到哪类缺陷，以及测试金字塔为什么底宽顶窄、什么情况下不成立

> 测试基础 · 测试分析 · 测试策略 · 约 7 分钟 · 10 月 19 日

## 本篇要点

1. 测试层级切的是“被测对象有多大”，每一层的区别在于哪些东西是真的、哪些是你摆上去的假设；层级也不等于测试类型，性能、安全等特性可以出现在任何一层。
2. 单元测试让一个函数单独跑起来，因此必须把数据库、网络、其他服务换成替身；替身的行为就是你的假设，所以真实依赖与假设不一致时，单元测试永远通过。
3. 集成测试只验两个真实组件之间的接缝，能抓字段名或类型不一致、事务未提交、超时未处理，但不重复验各自内部逻辑，也不保证整条链路正确。
4. 系统测试让整条链路都是真的，能抓“每段接缝都对、串起来却不对”的缺陷，代价是可控输入变少、失败信息变粗。
5. 前三层都拿同一份需求当尺子，需求写错会一起“正确地”通过；验收层换的是问题，从“做得对不对”变成“做的是不是业务要的东西”，最好由真实用户和业务方执行。
6. 金字塔的形状来自两边：低层单条用例覆盖窄所以条数多，高层慢、脆、定位粗所以条数少；它讲的是自动化的投资配比，并不意味着低层测试更重要。
7. 这条配比有个前提：上层测试昂贵、缓慢、脆弱；若高层测试又快又稳，低层就不必铺那么多，测试奖杯等形状就是把同一笔账换技术栈重算。
8. 判定一条用例放哪层，先写清这条风险需要哪样东西是真的，再放进能最低满足这个条件的那一层；高层只留串起来才出现的风险和业务判断。

---

上一篇写的是一份能交给别人执行的用例表。但用例表不回答一个更靠前的问题：这些用例该对着什么跑、由谁跑？

同一个“会员折扣算错”的问题，写成单元测试几十毫秒就跑完，失败时直接指到哪一行；写成从下单页点到支付成功的端到端用例要跑两分钟，失败时你只知道“金额不对”，还得自己找是哪一层错的。它们能抓到的缺陷也不同：有的事只有把系统连起来才会出现，有的事你把系统点一百遍也抓不到。

测试层级切的就是这件事。单元、集成、系统、验收这四层，切的是同一条链上的四段接缝，区别只有一条：**在这一层里，哪些东西是真的，哪些是你摆上去的假设。**

层级也不等于测试类型。性能、兼容、安全说的是“测什么特性”，可以出现在任何一层；层级说的是“被测对象有多大” [1]。先把四层的全貌摆出来：

| 层级 | 被测对象 | 拿什么当依据 | 典型能抓到的缺陷 | 通常由谁做 |
| --- | --- | --- | --- | --- |
| 单元（组件） | 一个函数、类或模块，其余部分换成替身 | 代码、详细设计、数据模型 | 算错、分支写反、边界没处理 | 写这段代码的开发者 |
| 集成 | 两个真实组件之间的调用与数据 | 架构图、接口与协议说明 | 字段名或类型对不上、事务没提交、超时没处理 | 开发者或测试人员 |
| 系统 | 整个系统的端到端行为和特性 | 需求、用例、状态图、用户手册 | 串起来才出现的错，界面与数据不一致 | 独立测试人员 |
| 验收 | 系统是否满足业务需要、能不能上线 | 业务流程、业务需求、合同与法规 | 需求本身写错或没写清、业务上不可接受 | 真实用户、业务方 |

下面用一个例子走完四层：会员下单享折扣，银卡九五折、金卡九折，折后满 99 元免运费。

## 单元层：其余世界换成了替身，这是它快的原因，也是它的盲区

要单独测一个 `discount(price, levelCode)` 函数，得先让它跑起来。它内部会去查用户等级，而单元测试里没有数据库，于是你写一个假对象，让它直接返回 `"GOLD"` 就完了。这种假对象就是**测试替身**：不连真东西，按你设定的样子返回结果。

速度快、定位精确都来自这一点：没有网络，没有界面，失败就指到那个函数。

盲区也来自这一点。**替身的行为是你写的，也就是你的假设。**如果真实用户服务返回的等级是数字 `3`，或者字段名叫 `member_level` 而不是 `levelCode`，你的单元测试永远通过，因为它从来没跟真实服务说过话。它证明的是“这段代码符合我对别人的假设”，不是“别人真的这么返回”。所以单元测试铺得再密，也回答不了下一层的问题。

## 集成层：只验两个真实组件之间的接缝

把折扣服务、用户服务、数据库这三样都换成真的，让它们互相调用一次，这就是集成测试。它不重复验各自的内部逻辑——单测验过的折扣算法在这里不用再验——只管接缝：字段传过来是什么类型、名字对不对、事务有没有提交、超时和重试有没有处理。

上一节那个类型不一致的问题，只有到这一层才会暴露，因为两边都是真的，假设被换成了事实。集成层常用的做法是“只让相邻的两个组件是真的，其余继续用替身”，代价是别的接缝上的问题仍然测不到。

## 系统层：整条链路都是真的，但你能控的和能看的都变少了

一个功能真正的风险，常出在“几条接缝各自都对、串起来却不对”的地方。

接着上面的例子：折扣服务算应付金额，运费服务判断要不要免运费。前者用折后价判断满 99 的门槛，后者用折前价判断。两个服务单测都过，接缝上的数据也都传对了，可是用户下单时，页面上显示的优惠明细是一个数，实际扣款是另一个数。**只有把整条链路跑一遍、把页面金额和实际收款比一比，才看得见它。**

系统层的依据是需求、用例、状态图这些描述“整个系统应该怎样”的东西，验的是整体行为和端到端任务，也包括性能、易用性这类必须在完整系统上才测得出的特性 [1]。代价是你能控制的输入变少、能看到的输出变粗：失败时它只说“金额不对”，不说哪儿不对。

## 验收层：它换了一个完全不同的问题

到这里必须说清一件容易被忽略的事：**前三层用的都是同一份需求文档当尺子。**需求写错，系统测试会照着错的需求一路通过。

举例：公司政策其实是“折扣与满减不可同享”，但需求文档没写，于是两者叠加了。单元、集成、系统三层全部通过，因为它们验的都是“有没有按文档实现”。这类问题只有换一个问题才能回答：**我们做出来的这个东西，是不是业务真正要的东西。**

这就是第一篇讲过的区分：前三层主要在回答“做得对不对”（验证），验收层回答“做的是不是对的东西”（确认）。ISTQB 把验收测试的目的写成“确认系统满足用户的业务需要、并证明它已可部署”，而且最好由预期用户来做 [1]。所以验收层不重复前三层的检查，它拿业务流程、真实数据和业务判断当依据。前三层全绿，也不等于可以上线。

## 金字塔：底宽顶窄，不是因为低层更重要

写成自动化，这三类测试的单价差很多：单元测试毫秒级、失败指到函数、不依赖网络和界面；端到端测试要把整个应用跑起来、点按钮、等页面，慢几个数量级，界面一改还可能挂掉 [3]。

同时，一条单元测试只覆盖一个判断，一条端到端测试却穿过所有层。**覆盖同样一片范围，低层需要的条数多，高层需要的条数少。**一边是单价，一边是条数，两头一起决定了形状：单元最多，中间的接口层次之，UI 层最少。这就是 Mike Cohn 在《Succeeding with Agile》里提出的测试金字塔 [2]，它讲的是自动化测试该往哪一层投。

![测试金字塔：底部 Unit 层最宽，往上 Service 与 UI 层越来越窄，旁边标着从慢到快、从省到贵的对照](https://www.martinfowler.com/bliki/images/testPyramid/test-pyramid.png)

由此得到三条做法：能写成单元测试的不要放到 UI 层；单元测试覆盖不到的接缝，用集成测试补；UI 测试少而精，只覆盖必须端到端才能验的东西 [3]。对写用例的人还有一句更实用的：**把一条用例尽量放低，低到那个风险仍然抓得住为止。**反过来，把自动化全堆在 UI 层，就是所谓“冰淇淋筒”，也是记录回放式 UI 自动化的常见结局 [2]。

这个形状有个前提：上层测试昂贵、缓慢、脆弱。Fowler 自己标出了例外——如果你的高层测试又快又稳、改动成本又低，低层就不必铺那么多 [2]。后来出现的测试奖杯、蜂巢模型，就是把这个成本收益判断换到别的技术栈里重算的结果 [4]，金字塔并不因此失效。

## 怎么决定一个功能该在哪一层测

把上面的内容合成三步：

1. **先把风险写成“必须有哪样东西是真的”。**比如“用户等级字段的类型和代码预期一致”“页面显示的金额和实际扣款一致”“折扣不与满减叠加是公司政策”。
2. **找最低的、能让这个条件真实存在的那一层。**只有当数据库是真的，才验得出字段类型；只有当整条链路串起来，才能比对页面金额和扣款；只有当业务方在场，才判得了政策。
3. **高层只留“串起来才出现”和“业务上说不通”的东西。**其余的留在低层，高层才跑得快、失败时才说得清。

按这三步回看你手头的用例表，很多条目会自己找到该去的层——也会发现有些风险，四层里没有任何一层能替你回答。

## 术语表

- 测试层级：按被测对象有多大把测试分成单元、集成、系统、验收几段，每段负责一类只在这个尺度上才出现的缺陷。
- 测试替身：测试里替代真实依赖的假对象，按你设定的样子返回结果，让被测代码能脱离数据库和网络单独运行；它的返回值就是你的假设。
- 定位精度：测试失败时能把问题指到多细的范围，单元层最细，端到端最粗。
- 测试金字塔：单元最多、接口层次之、UI 层最少的自动化用例配比模型，形状由每层单条用例覆盖的范围和单价共同决定。
- 冰淇淋筒：把绝大多数自动化压在最贵的 UI 层的反过来做法，属于要避免的情况。

## 来源

1. [ISTQB Certified Tester Foundation Level Syllabus v4.0.1 — 2.2 节测试层级定义：被测对象、测试依据、典型缺陷与验收测试目的](https://istqb.org/wp-content/uploads/2024/11/ISTQB%5FCTFL%5FSyllabus%5Fv4.0.1.pdf)
2. [Martin Fowler, Test Pyramid — 金字塔针对自动化测试组合，说明 UI 端到端测试为何慢而脆及其前提假设](https://www.martinfowler.com/bliki/TestPyramid.html)
3. [The Way of the Web Tester（试读章节）— 三层金字塔的速度、精度与覆盖差异，以及三条配比规则](https://media.pragprog.com/titles/jrtest/pyramid.pdf)
4. [Martin Fowler, On the Diverse And Fantastical Shapes of Testing — 测试奖杯与蜂巢模型对金字塔的修正](https://www.martinfowler.com/articles/2021-test-shapes.html)

---

原文：https://pangzhengboyin.com/articles/test-levels-and-test-pyramid-8463dada

> **庞征博引** · 想学的，慢慢都会
>
> 庞征博引是把想学的东西写成连载的 AI 学习工具。说出想学什么，它会先了解你的基础，再把主题写成一篇篇 5–10 分钟能读完的文章；边读边问，接下来学什么跟着你走。这篇就是这样写出来的。
>
> 开始你自己的连载 → https://pangzhengboyin.com
