等价类划分法:原理与定义

等价类划分,说白了就是「把输入数据分堆」。

我刚开始做测试那会儿,总觉得要测遍所有输入才放心。后来带我的老工程师跟我说:「你想想看,车速从 0 到 300 km/h,难道每个值都要测一遍?」

当然不是。很多输入值的行为是「等价」的。比如车速 50 km/h 和 51 km/h,对系统来说处理逻辑完全一样。那我们只需要从每个等价类里挑一个代表值来测就行。

这就是等价类划分的核心思想:把无穷的输入域,划分成有限的等价类

定义:等价类是指输入域中,程序处理方式相同的一组数据。从每个等价类中选取一个代表值进行测试,就能覆盖该类所有可能的情况。

嗯,这里要注意:等价类划分不是随便分的。你得理解系统是怎么处理这些输入的。我见过有人把 0-100 km/h 划成一个等价类,结果系统在 60 km/h 有个特殊逻辑——这就漏测了。

有效等价类与无效等价类

等价类分两种:有效的和无效的。

有效等价类,就是符合需求、系统应该正常处理的输入。比如车速信号范围是 0-200 km/h,那 0 到 200 之间的值就是有效等价类。

无效等价类,就是不符合需求、系统应该拒绝或报错的输入。比如负车速、超过 200 km/h 的值。

我个人的习惯是:先列有效等价类,再列无效等价类。而且无效等价类往往比有效等价类多。为什么?因为系统出 bug 的地方,通常就在边界和异常输入上。

输入条件 有效等价类 无效等价类
车速范围 0 ≤ v ≤ 200 km/h v < 0, v > 200 km/h
信号类型 CAN 信号 非 CAN 信号、空信号
信号周期 10 ms - 100 ms < 10 ms, > 100 ms

我的经验:无效等价类往往比有效等价类更容易发现 bug。我曾经测过一个项目,有效等价类全过了,结果在无效等价类里抓到了 3 个严重缺陷。所以别偷懒,无效等价类一定要列全。

边界值分析结合

等价类划分和边界值分析,就像咖啡和奶精——分开也能用,但合在一起才香。

为什么?因为大量的 bug 都发生在边界上。你想想看,车速从 199 km/h 跳到 200 km/h,或者从 0 跳到负值,这些临界点最容易出问题。

我的做法是:先用等价类划分确定「测哪些范围」,再用边界值分析确定「测哪些具体值」

比如车速 0-200 km/h 这个有效等价类,边界值分析会让我们测:

  • 最小值:0 km/h
  • 略大于最小值:1 km/h
  • 略小于最大值:199 km/h
  • 最大值:200 km/h

无效等价类也一样:

  • 略小于 0:-1 km/h
  • 略大于 200:201 km/h

我记得有一次,系统在车速 0 km/h 时有个特殊处理——要显示「停车」图标。结果测试只测了 10 km/h、50 km/h 这些值,完全没发现 0 km/h 的逻辑有问题。后来加上边界值分析,这个问题才被揪出来。

注意:边界值分析不是只测边界点。对于有多个边界的等价类,每个边界都要测。我曾经见过一个测试用例只测了上边界,漏掉了下边界,结果下边界附近出了个严重 bug。

实战案例:车速信号处理

好了,理论讲完了,咱们来点实际的。

假设我们有一个车速信号处理模块,输入是 CAN 总线上的车速信号,输出是仪表盘显示的车速值。需求是这样的:

  • 车速范围:0 - 200 km/h
  • 信号分辨率:0.1 km/h
  • 信号更新周期:20 ms
  • 超时判断:超过 100 ms 无信号,报超时故障
  • 信号无效判断:车速 > 200 km/h 或 < 0 km/h,报无效信号故障

咱们来设计测试用例。

第一步:划分等价类

输入条件 有效等价类 无效等价类
车速值 0 ≤ v ≤ 200 v < 0, v > 200
信号周期 20 ms ± 5 ms < 15 ms, > 25 ms
信号超时 ≤ 100 ms 有信号 > 100 ms 无信号

第二步:结合边界值分析

车速值边界:0, 0.1, 199.9, 200, -0.1, 200.1

信号周期边界:15, 16, 24, 25, 14, 26

超时边界:100 ms, 101 ms

第三步:编写测试用例

// 测试用例 1:正常车速,最小值
输入:车速 = 0.0 km/h,周期 = 20 ms
预期:显示 0.0 km/h,无故障

// 测试用例 2:正常车速,最大值
输入:车速 = 200.0 km/h,周期 = 20 ms
预期:显示 200.0 km/h,无故障

// 测试用例 3:无效车速,负值
输入:车速 = -0.1 km/h,周期 = 20 ms
预期:显示无效,报「无效信号」故障

// 测试用例 4:无效车速,超上限
输入:车速 = 200.1 km/h,周期 = 20 ms
预期:显示无效,报「无效信号」故障

// 测试用例 5:信号超时
输入:车速 = 50.0 km/h,周期 = 101 ms
预期:保持上次有效值,报「超时」故障

// 测试用例 6:信号周期异常,过快
输入:车速 = 50.0 km/h,周期 = 14 ms
预期:显示 50.0 km/h,报「周期异常」故障

关键点:每个测试用例只改变一个变量。比如测车速边界时,信号周期保持正常。测周期边界时,车速保持正常。这样出了问题,你一眼就能看出是哪个条件导致的。

我曾经犯过一个错误:在一个测试用例里同时改了车速和周期,结果报了个故障,我花了半天才查出来是周期的问题,不是车速的问题。从那以后,我坚持「一次只变一个」的原则。

好了,这就是等价类划分法的实战应用。你想想看,如果不做等价类划分,光车速值就有 2001 个可能值(0.0 到 200.0,步长 0.1),再加上周期、超时这些条件,测试用例数量会爆炸。但用了等价类划分,我们只需要十几个用例就能覆盖所有情况。

这就是等价类划分的魅力——用最少的用例,发现最多的 bug