# 等价类与边界值：一个输入框的取值数不完，为什么测几条就够

讲清等价类划分凭什么只测一个代表、边界值分析为什么专抓 off-by-one，以及两者合起来怎样把无穷取值收敛成七条用例

> 测试用例设计 · 测试分析 · 黑盒测试 · 约 7 分钟 · 09 月 19 日

## 本篇要点

1. 等价类划分依据的是“预期系统对同一堆取值走同一条处理路径”，把数不完的取值压成若干堆，每堆取一个代表即可；覆盖率按“每个类是否至少被一条用例踩到”算。
2. 同一个输入通常至少要分出有效类、偏低无效类、偏高无效类；格式、类型、空值这类处理路径不同的值要单独划类，不能和前几类混在一起。
3. “类内处理方式相同”是预期而非已验证的事实，会在前后端校验不一致、极端数值走不同计算分支时失效，高风险输入需要额外多取几个值。
4. 边界值分析只适用于有顺序的取值，专门针对 off-by-one 这类缺陷——比较符号写错、边界挪位，只在边界点上表现出来，中间值永远测不出。
5. 2 值 BVA 在每处分界取两侧各一个值；3 值 BVA 再补上边界内侧的邻居。ISTQB 白皮书的例子显示，当 `x <= 10` 被误写成 `x == 10`，2 值取的 10 和 11 都发现不了，必须靠内侧的 9。
6. ISTQB 意义上的边界值是每个等价类自己的最小值和最大值，不是两类之间的那条“缝”；相邻两类会共用同一个点，物理上不可达的外侧值直接删掉。
7. 一个 1 到 100 的抵扣比例输入，用 3 值 BVA 加上格式类，一共七条用例就能覆盖下界外、下界、下界内侧、有效内点、上界内侧、上界、上界外，外加格式错误。
8. 输出同样可以划等价类、取边界值，比如“抵扣后总额不得为负”的边界落在输出上；而 EP 加 BVA 只处理单个输入的取舍，多参数组合要靠别的方法。
9. 需求对端点含不含没写清时，边界上两条用例的预期会完全反过来，必须先问清再写。

---

上一篇结尾留下一句话：把需求拆成测试点之后，下一步是挑测试设计方法，把每个点变成具体的数据和预期。

真到这一步，先撞上的是一道算术题。假设有个字段要求填“18 到 65 之间的整数”，你不可能把 18、19、20……65 全部试一遍；再想想那个优惠码功能，商品小计是金额，金额能精确到分，取值根本数不完。可实际工作里，一个这样的字段往往只写几条用例就收工了。

这不是偷懒，而是有两套方法在支撑“少测也能测准”。它们一个负责把无穷的取值压成几堆，另一个负责决定每一堆里具体挑哪几个值。

## 等价类划分：凭什么只测一个代表就够

等价类划分（Equivalence Partitioning，EP）的做法是：把输入可能取的值得按“系统会怎么处理它”分成若干堆，每一堆叫一个等价类。同一堆里的值，预期系统走的是同一条处理路径，得到同一种结果 [1][2]。

那么这一堆值里，随便挑一个来测就够了。背后的推理很直接：如果测其中一个值发现了缺陷，同一堆里其他值也一定会撞上同一个缺陷；反过来，测了一个没问题，同一堆里其他的也应该没问题。

以一个 18 到 65 的年龄字段为例，至少有三堆：

- **有效类**：18 到 65 之间的整数，系统应当接受；
- **无效类（偏低）**：小于 18 的整数，系统应当拒绝；
- **无效类（偏高）**：大于 65 的整数，系统应当拒绝。

从每一堆里各挑一个值，就拿到了 EP 意义上的全覆盖：三个类，三条用例，覆盖的是三种处理结果。

这里有两个容易踩空的地方。

**第一，划类的依据是“处理方式相同”，不是“数值看着差不多”。** 还有一堆值得单独划出来的值：非数字内容、空值、前后带空格、`50%` 这类带多余符号的写法。它们都“不是 18 到 65”，但系统对它们的处理路径常常完全不一样——有的被格式校验拦在前面，有的进到解析逻辑里报另一种错。如果把这些全塞进同一个无效类，你只用一条用例代表它们，就会漏掉整类情况。

**第二，“同一堆走同一条路径”是预期，不是你验证过的事实。** 常见的破口是：前端校验一套规则，绕开界面直接调接口走另一套；或者金额特别大、特别小时，系统内部换了另一种精度或舍入方式。所以对于风险高的输入，即便已经有一堆，也值得在里面多挑一两个差异较大的值。

## 边界值分析：为什么中间值永远抓不到那个 bug

假设开发按需求写出了 `if (age >= 18)`，实际代码里敲成了 `if (age > 18)`。一个字符之差，结果就是 18 岁的人被拒。你用 25 测一百遍都不会有事，因为 25 在两种写法下都通过。这个缺陷只在边界那一个点上露头。

这是最典型的一类缺陷，名字叫 **off-by-one（差一）**：比较符号用错、边界挪了一格、某一端干脆忘了判断。它就藏在分界的位置上，所以测试经验里有一条共识——缺陷在区间的边缘聚集，而不是在中间 [1][3]。

**边界值分析（Boundary Value Analysis，BVA）** 就是专门去踩这些边缘的。做法派生出两个版本 [3][4]：

对 1 到 100 这个区间，

- **2 值 BVA**：每处分界，两侧各取一个值。于是拿 `0`（下界外）、`1`（下界）、`100`（上界）、`101`（上界外），四条就能覆盖两处分界。
- **3 值 BVA**：再补上边界内侧的邻居，也就是加上 `2` 和 `99`，一共六条。

内侧那两个值不是凑数的。ISTQB 的白皮书给了一个很干净的说明：需求是 `if (x <= 10)`，开发把它误写成 `if (x == 10)`。这时候 2 值 BVA 取的 `10` 和 `11` 都发现不了问题——`x = 10` 时两种写法行为一致，`x = 11` 时两种写法都判 false。补上 `9` 才会露馅：需求说 9 应该通过，而 `x == 10` 会把它挡掉 [4]。

那什么时候用 2 值、什么时候用 3 值？2 值便宜，绝大多数场景够用；金额、权限、合规一类后果严重的边界，值得多花那两条用例。

## 边界值是每个类自己的端点，不是两类之间的“缝”

很多人把边界理解成两个区间之间那条分界线。按 ISTQB 的说法，这样理解是不对的 [4]。看一个用户名字段，允许 6 到 15 个字符，取值域被分成三段：0 到 5（太短）、6 到 15（合法）、16 及以上（太长）。这里说的边界值不是“5 和 6 之间的缝”，而是**每个等价类自己的最小值和最大值**：

- 0 到 5 这一段：边界值是 `0` 和 `5`；
- 6 到 15 这一段：边界值是 `6` 和 `15`；
- 16 及以上这一段：只有最小值 `16`，因为它没有上界。

把它们合起来，2 值 BVA 要测的就是 `0、5、6、15、16` 五个值 [4]。可以看出，紧挨着某一类边界的那个外侧值，本身往往就是相邻那一类的边界值——两段共用一个点。另外，如果某个外侧值物理上不可能（比如长度不可能是 `-1`），就直接删掉，不用硬造。

还有一条适用范围：**BVA 只能用在有顺序的取值上**。数字、日期、金额、字符长度、文件大小、优先级，这些都排得出大小。省份、颜色、渠道名这类枚举值没有顺序，也就没有边界可言，只能用 EP。

## 合起来用：拿优惠码走一遍

上一篇那个优惠码需求里有一条：“有效优惠码按指定比例抵扣商品小计。”把抵扣比例做成 1 到 100 的整数输入，套上这两套方法，流程是这样：

1. **划类**：有效类 = 1 到 100 的整数；无效类 = 小于 1、大于 100、非整数、非数字、带空格或多余字符。
2. **每个类挑一个内点**：比如有效类取 `50`。
3. **每处分界取 2 或 3 个值**：下界取 `0`、`1`（3 值再加 `2`）；上界取 `100`、`101`（3 值再加 `99`）。
4. **合并去重，逐条写预期**。

| 取值 | 属于哪种 | 预期 |
| --- | --- | --- |
| `0` | 下界外侧 | 拒绝，提示取值范围 |
| `1` | 下界 | 接受，抵扣 1% |
| `2` | 下界内侧 | 接受，抵扣 2% |
| `50` | 有效类内点 | 接受，抵扣 50% |
| `99` | 上界内侧 | 接受，抵扣 99% |
| `100` | 上界 | 接受，抵扣 100% |
| `101` | 上界外侧 | 拒绝 |
| `"abc"`、空值、`" 50"` | 格式类 | 拒绝，走格式错误提示 |

一个理论上有一百个合法取值、还外加无数种乱填的输入框，就这样被压成了七条用例——而且每一条都说得清理由，不是随口挑的。

同一个需求里还有一条“抵扣后订单总额不得为负”。这条规则的边界不在输入上，而在**输出**上：要盯的是抵扣额刚好等于商品小计的那个点，总额应当停在 0，而不是变成负数。输出一样可以划等价类、取边界值，别只盯着输入框 [2]。

## 这套方法管到哪里为止

EP 加 BVA 处理的是**单个输入**的取舍。一旦功能依赖多个参数的组合，比如“用户等级 × 订单金额 × 是否首单”各三档，按“每个类各取一次”的规则来构造用例，只能得到三条，组合出来的 27 种情形里绝大多数根本没碰。这种情况要靠别的方法，留到以后再说。

更要紧的一层是：EP 的全部效力都建立在“类内处理方式相同”这个预期上。这个预期一旦错了，你漏掉的不是一条用例，而是整整一类。所以这两套方法给的是取舍的底线，不是风险的终点——真正高风险的地方，值得在底线之上再多砸几个值。

最后一件事，边界值会把上一篇讲过的动作价值放大。如果需求写的是“满 100 元免运费”，而你没先确认刚好 100 元算不算，那么 `100` 和 `99` 这两条用例的预期就是完全反过来的：一个说该免，一个说该收。差一个字，两条用例全废。

## 术语表

- 等价类划分（Equivalence Partitioning）：把输入取值按“预期系统会同样处理”分成若干堆，每堆取一个代表来测，用这个假设换掉大量重复用例。
- 有效类与无效类：系统应当接受的取值构成的类叫有效类，应当拒绝的（偏低、偏高、格式不对）叫无效类，一个输入通常至少有一个有效类和两个以上无效类。
- 边界值分析（Boundary Value Analysis）：在等价类的端点上取值，专门用来暴露 off-by-one 这类只在边界点上显形的缺陷，只能用于有顺序的取值。
- 2 值 BVA 与 3 值 BVA：前者在每处分界取两侧各一个值，后者再补上边界内侧的邻居，覆盖更强但用例更多。
- off-by-one（差一）：比较符号用错（`>` 与 `>=` 混用）、边界挪一格或漏判某一端，导致结果只在边界那一个取值上出错。

## 来源

1. [ISTQB Foundation Level 大纲 4.2 节 — 等价类划分与边界值分析的官方定义](https://astqb.org/4-2-black-box-techniques/)
2. [Decision Tables, Equivalence Partitioning, BVA: ISTQB Guide — 有效类/无效类的划分习惯与两种方法的分工](https://www.istqb.guru/decision-tables-equivalence-partitioning-boundary-value-analysis/)
3. [CTFL 大纲原文摘录 — 覆盖准则、2 值与 3 值 BVA 的差别，以及 x <= 10 被误写为 x == 10 的示例](https://www.smartesting.com/wp-content/uploads/2024/08/Exercise-0-Exerpt-of-the-ISTQB-CTFL-Syllabus.pdf)
4. [ISTQB 边界值分析白皮书 — 分步流程、区间 6–15 的完整算例，以及“边界值不等于分界线”的澄清](https://istqb.org/wp-content/uploads/2025/10/Boundary-Value-Analysis-white-paper.pdf)

---

原文：https://pangzhengboyin.com/articles/equivalence-partitioning-boundary-value-analysis-5a1132e6

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