上一篇结尾留下一句话:把需求拆成测试点之后,下一步是挑测试设计方法,把每个点变成具体的数据和预期。
真到这一步,先撞上的是一道算术题。假设有个字段要求填“18 到 65 之间的整数”,你不可能把 18、19、20……65 全部试一遍;再想想那个优惠码功能,商品小计是金额,金额能精确到分,取值根本数不完。可实际工作里,一个这样的字段往往只写几条用例就收工了。
这不是偷懒,而是有两套方法在支撑“少测也能测准”。它们一个负责把无穷的取值压成几堆,另一个负责决定每一堆里具体挑哪几个值。
等价类划分:凭什么只测一个代表就够
等价类划分(Equivalence Partitioning,EP)的做法是:把输入可能取的值得按“系统会怎么处理它”分成若干堆,每一堆叫一个等价类。同一堆里的值,预期系统走的是同一条处理路径,得到同一种结果 12。
那么这一堆值里,随便挑一个来测就够了。背后的推理很直接:如果测其中一个值发现了缺陷,同一堆里其他值也一定会撞上同一个缺陷;反过来,测了一个没问题,同一堆里其他的也应该没问题。
以一个 18 到 65 的年龄字段为例,至少有三堆:
- 有效类:18 到 65 之间的整数,系统应当接受;
- 无效类(偏低):小于 18 的整数,系统应当拒绝;
- 无效类(偏高):大于 65 的整数,系统应当拒绝。
从每一堆里各挑一个值,就拿到了 EP 意义上的全覆盖:三个类,三条用例,覆盖的是三种处理结果。
这里有两个容易踩空的地方。
第一,划类的依据是“处理方式相同”,不是“数值看着差不多”。 还有一堆值得单独划出来的值:非数字内容、空值、前后带空格、50% 这类带多余符号的写法。它们都“不是 18 到 65”,但系统对它们的处理路径常常完全不一样——有的被格式校验拦在前面,有的进到解析逻辑里报另一种错。如果把这些全塞进同一个无效类,你只用一条用例代表它们,就会漏掉整类情况。
第二,“同一堆走同一条路径”是预期,不是你验证过的事实。 常见的破口是:前端校验一套规则,绕开界面直接调接口走另一套;或者金额特别大、特别小时,系统内部换了另一种精度或舍入方式。所以对于风险高的输入,即便已经有一堆,也值得在里面多挑一两个差异较大的值。
边界值分析:为什么中间值永远抓不到那个 bug
假设开发按需求写出了 if (age >= 18),实际代码里敲成了 if (age > 18)。一个字符之差,结果就是 18 岁的人被拒。你用 25 测一百遍都不会有事,因为 25 在两种写法下都通过。这个缺陷只在边界那一个点上露头。
这是最典型的一类缺陷,名字叫 off-by-one(差一):比较符号用错、边界挪了一格、某一端干脆忘了判断。它就藏在分界的位置上,所以测试经验里有一条共识——缺陷在区间的边缘聚集,而不是在中间 13。
边界值分析(Boundary Value Analysis,BVA) 就是专门去踩这些边缘的。做法派生出两个版本 34:
对 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 到 100 的整数;无效类 = 小于 1、大于 100、非整数、非数字、带空格或多余字符。
- 每个类挑一个内点:比如有效类取
50。 - 每处分界取 2 或 3 个值:下界取
0、1(3 值再加2);上界取100、101(3 值再加99)。 - 合并去重,逐条写预期。
| 取值 | 属于哪种 | 预期 |
|---|---|---|
0 | 下界外侧 | 拒绝,提示取值范围 |
1 | 下界 | 接受,抵扣 1% |
2 | 下界内侧 | 接受,抵扣 2% |
50 | 有效类内点 | 接受,抵扣 50% |
99 | 上界内侧 | 接受,抵扣 99% |
100 | 上界 | 接受,抵扣 100% |
101 | 上界外侧 | 拒绝 |
"abc"、空值、" 50" | 格式类 | 拒绝,走格式错误提示 |
一个理论上有一百个合法取值、还外加无数种乱填的输入框,就这样被压成了七条用例——而且每一条都说得清理由,不是随口挑的。
同一个需求里还有一条“抵扣后订单总额不得为负”。这条规则的边界不在输入上,而在 输出 上:要盯的是抵扣额刚好等于商品小计的那个点,总额应当停在 0,而不是变成负数。输出一样可以划等价类、取边界值,别只盯着输入框 2。
这套方法管到哪里为止
EP 加 BVA 处理的是 单个输入 的取舍。一旦功能依赖多个参数的组合,比如“用户等级 × 订单金额 × 是否首单”各三档,按“每个类各取一次”的规则来构造用例,只能得到三条,组合出来的 27 种情形里绝大多数根本没碰。这种情况要靠别的方法,留到以后再说。
更要紧的一层是:EP 的全部效力都建立在“类内处理方式相同”这个预期上。这个预期一旦错了,你漏掉的不是一条用例,而是整整一类。所以这两套方法给的是取舍的底线,不是风险的终点——真正高风险的地方,值得在底线之上再多砸几个值。
最后一件事,边界值会把上一篇讲过的动作价值放大。如果需求写的是“满 100 元免运费”,而你没先确认刚好 100 元算不算,那么 100 和 99 这两条用例的预期就是完全反过来的:一个说该免,一个说该收。差一个字,两条用例全废。