3、测试用例设计方法(上):等价类划分法、边界值分析法、因果图法
大家好,我是老张。今天咱们聊聊测试用例设计。说实话,这可能是整个黑盒测试里最核心的功夫了。
你想想看,一个系统有成百上千个输入。你不可能把所有组合都测一遍。那怎么办?就得靠方法。我这些年带团队,发现很多新人上来就凭感觉写用例。结果呢?要么漏测,要么重复。今天我把三个最基础、也最实用的方法掰开揉碎了讲给你听。
3.1 等价类划分法:把无穷变成有限
等价类划分,说白了就是「分堆」。把输入数据分成几个类别。每个类别里,挑一个代表测就行了。
为什么能这么干?因为程序处理同类数据的方式是一样的。你测了1号,2号、3号就不用测了。这叫「等效」。
3.1.1 有效等价类 vs 无效等价类
这里有个关键点:一定要分两种。
- 有效等价类:符合需求、程序应该正常处理的输入
- 无效等价类:不符合需求、程序应该拒绝或报错的输入
我在项目中遇到过一件事。有个同事只测了有效数据。结果上线后,用户输入了一个负数,系统直接崩溃了。嗯,这就是典型的无效等价类没覆盖到。
核心原则:每个无效等价类,要单独设计一个测试用例。因为程序处理不同错误的方式可能不一样。你把它放一起测,可能第一个错误就挡住了后面的。
3.1.2 划分原则
怎么分?我一般按这几个维度来:
- 数值范围:比如年龄 0-150,可以分成 0-150(有效)、<0(无效)、>150(无效)
- 枚举值:比如性别「男、女」,有效就是这两个,无效就是其他所有
- 布尔值:true 和 false,各算一个
- 字符串长度:比如密码 6-16 位,有效是 6-16,无效是 <6 和 >16
- 格式规则:比如邮箱必须包含 @,有效是带 @ 的,无效是不带的
3.1.3 实战案例:用户注册模块
咱们拿一个注册功能练练手。需求是这样的:
- 用户名:6-12位字母或数字
- 密码:8-16位,必须包含字母和数字
- 年龄:18-60岁整数
我一般会画个表:
| 输入项 | 有效等价类 | 无效等价类 |
|---|---|---|
| 用户名 | 6-12位字母数字组合 | <6位、>12位、含特殊字符、含中文 |
| 密码 | 8-16位,含字母和数字 | <8位、>16位、纯字母、纯数字、含特殊字符 |
| 年龄 | 18-60整数 | <18、>60、小数、非数字 |
你看,原来可能有几万种组合。现在呢?有效类3个,无效类加起来10个左右。13个用例就搞定了。
我的习惯:先列有效等价类,再列无效等价类。无效的每个都要单独测。别偷懒,我吃过亏。
3.2 边界值分析法:bug 最喜欢藏在边界上
等价类划分有个弱点。它假设同类数据表现一致。但现实不是这样的。程序在边界处最容易出问题。
为什么会这样?因为程序员写代码时,经常用 <= 和 <。差一个等号,结果天差地别。边界值分析法就是专门抓这种 bug 的。
3.2.1 边界值的基本概念
对于一个范围 [1, 100],边界值包括:
- 上点:1 和 100(边界上的点)
- 离点:0 和 101(边界附近最近的点)
- 内点:50(范围内的任意点)
我一般习惯取「最小值、最小值-1、最小值+1、最大值、最大值-1、最大值+1」。一共6个值。
3.2.2 边界值 vs 等价类:怎么配合?
这两个方法不是二选一,是搭档。我通常的做法是:
- 先用等价类划分,确定大类
- 再对每个边界,用边界值分析法补充用例
举个例子。年龄 18-60:
- 等价类:有效(18-60)、无效(<18、>60)
- 边界值:17、18、19、59、60、61
你看,边界值把等价类的边界挖得更细了。
注意:边界值不只适用于数值。字符串长度、列表元素个数、文件大小……只要是有「边界」的地方,都适用。
3.2.3 实战案例:购物车数量限制
需求:购物车中单个商品数量 1-99 件。
我设计的用例:
| 测试数据 | 预期结果 | 说明 |
|---|---|---|
| 0 | 提示「数量不能为0」 | 下边界-1 |
| 1 | 添加成功 | 下边界 |
| 2 | 添加成功 | 下边界+1 |
| 98 | 添加成功 | 上边界-1 |
| 99 | 添加成功 | 上边界 |
| 100 | 提示「数量不能超过99」 | 上边界+1 |
我曾经遇到过一个问题。开发把 if (count > 0 && count < 100) 写成了 if (count >= 0 && count <= 100)。结果 0 和 100 都通过了。边界值测试直接抓到了这个 bug。
3.3 因果图法:理清复杂逻辑关系
等价类和边界值,主要针对单个输入。但现实中的系统,输入之间是有关系的。比如:
- 「如果选了A,就不能选B」
- 「只有满足C和D,才能执行E」
这时候,因果图法就派上用场了。
3.3.1 因果图的核心思想
因果图,说白了就是画逻辑关系图。
- 因:输入条件
- 果:输出结果
- 关系:与、或、非、异或等
我一般分四步走:
- 找出所有输入(因)和输出(果)
- 画出因果关系图
- 把图转成判定表
- 根据判定表写用例
3.3.2 常见的逻辑关系
| 关系 | 符号 | 说明 | 例子 |
|---|---|---|---|
| 恒等 | = | 因成立,果就成立 | 输入正确密码 → 登录成功 |
| 非 | ~ | 因不成立,果才成立 | 不是管理员 → 隐藏管理菜单 |
| 与 | ∧ | 所有因都成立,果才成立 | 有账号 + 有密码 → 可以登录 |
| 或 | ∨ | 任一因成立,果就成立 | 刷卡成功 或 扫码成功 → 开门 |
| 异或 | ⊕ | 有且仅有一个因成立 | 选「男」就不能选「女」 |
3.3.3 实战案例:优惠券发放规则
需求是这样的:
- 用户等级是VIP,或者消费金额超过1000元
- 并且用户没有领取过该优惠券
- 满足以上条件,发放优惠券
咱们来画因果图。
因:
- A:用户是VIP
- B:消费金额 > 1000
- C:未领取过该优惠券
果:
- X:发放优惠券
关系:
- (A ∨ B) ∧ C → X
转成判定表:
| A | B | C | X |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 0 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 0 | 0 |
| 1 | 1 | 1 | 1 |
你看,8种组合,一目了然。哪些能发券?第4、6、8行。哪些不能?剩下的5行。
我的经验:因果图法适合逻辑复杂的场景。但别滥用。如果输入条件超过5个,判定表会变得很大。这时候可以考虑用正交实验法,咱们后面会讲。
3.4 三种方法的配合使用
最后,我聊聊这三个方法怎么搭配。
- 等价类划分:先分大类,确定测试范围
- 边界值分析法:在边界上深挖,抓边界bug
- 因果图法:处理输入之间的逻辑关系
我一般的设计流程是:
- 用等价类划分,列出所有输入的大类
- 对每个数值类输入,补充边界值
- 如果输入之间有逻辑关系,用因果图梳理
- 合并去重,得到最终的测试用例集
举个例子。一个登录功能:
- 用户名:等价类 + 边界值(长度边界)
- 密码:等价类 + 边界值(长度边界)
- 验证码:等价类(正确/错误/过期)
- 登录按钮:因果图(用户名正确 ∧ 密码正确 ∧ 验证码正确 → 登录成功)
这样一套组合拳下来,覆盖率基本能到90%以上。
最后提醒一句:方法只是工具,别为了用方法而用方法。我见过有人把等价类分得特别细,结果用例数量翻了好几倍。没必要。核心是「用最少的用例,覆盖最多的场景」。
好了,今天先聊到这儿。下一讲咱们继续聊场景法、正交实验法和错误推测法。到时候见。