软件测试的终极难题不是找不到缺陷,而是永远找不到所有缺陷。以一段接受年龄输入的程序为例,合法范围是十八到六十岁,那么你打算输入哪些年龄值来测试?十八、十九、三十五、五十九、六十?还是十七、十八、十九、三十五、五十九、六十、六十一?更关键的是,凭什么选这些值而不选别的?如果换一个开发人员来选,会不会完全不同?这就引出了测试领域最基础也是最核心的问题:测试用例的选取依据,到底应该是什么。
等价类划分法给出了第一个答案。它的核心思想是将程序的输入域划分为若干子集,每个子集等价于其他所有成员——即从该子集中任选一个代表值进行测试,效果等同于测试该子集中的任何其他值。如果某个代表值能发现缺陷,则同一等价类中的其他值也有极大概率发现相同的缺陷;反之,如果代表值测试通过,则该等价类中其他值大概率也能通过。等价类划分的目的不是消灭所有测试,而是用最少的测试用例实现最大的缺陷发现效率,从而在测试成本与测试质量之间找到最优平衡。
等价类分为有效等价类和无效等价类两种。有效等价类指的是符合程序规格说明的、合理的、有意义的输入数据构成的集合。无效等价类则相反,是那些不符合规格说明、不合理的、无意义的输入数据。按软件工程的标准表述,每个等价类应当满足可区分性和完备性两条基本属性——可区分性要求不同等价类之间的行为差异必须大到足以被观测,完备性要求所有可能的输入值都必须被归入某个等价类,不能遗漏。
边界值分析法则进一步聚焦于等价类之间的分界点。程序错误并不均匀分布在输入域中,经验表明,绝大多数错误集中在输入域的边界附近,而非内部区域。边界值分析的基本假设是:如果一个程序在边界值上能正确处理输入,那么它在边界内部的典型值上也能正确处理。因此,测试用例应优先选取刚好等于边界值、刚好大于边界值、刚好小于边界值的输入数据,而不是均匀撒在等价类内部。
这两种方法的关系并非并列或替代,而是递进与互补。等价类划分解决的是测试覆盖面的问题——确保每一种输入类型都被照顾到;边界值分析解决的是测试精度的问题——确保最容易出错的边界区域被重点关照。把二者结合起来,就构成了黑盒测试中最成熟的测试用例设计策略。
等价类划分和边界值分析根植于软件工程对测试成本控制的长期探索。上世纪七十年代,Glenford Myers 在《软件测试的艺术》中首次系统阐述了等价类划分的思想,指出穷举测试在绝大多数场景下不可行,测试的本质是从无限输入空间中抽取有限样本。Boris Beizer 则将硬件测试中边界故障集中的观察引入软件领域,在《软件测试技术》中提出边界值分析并给出了具体的选取规则。随后边界值分析被纳入 IEEE 829 测试文档标准。掌握这两种方法不只学会两种测试技术,更是理解软件测试思维起点的逻辑根基。
等价类划分的背后是一个朴素的数学直觉:如果一个程序对某个输入值的处理逻辑和对另一个输入值的处理逻辑完全相同,那么这两个输入值在逻辑上是等价的。这里的等价不是数值相等,而是行为等价——程序对二者的执行路径、判断分支、状态变更完全一致。等价类的本质,就是对程序控制流图上的执行路径进行输入域划分。
以一段验证手机号码的程序为例:规则是十一位数字、第一位必须为一。那么输入"一二三四五六七八九零一二"和输入"一三八零零零零零零零零"走的是同一条验证路径吗?不是——前者长度不合法,后者长度合法但第一位不符合要求。有效的测试策略是将输入域划分为以下等价类:十一位且以一开头的数字串、长度不足十一位的字符串、长度超过十一位的字符串、长度正确但非数字字符的字符串、长度正确但首位不是一的数字串,等等。每个等价类对应一条独立的程序执行路径。
从形式化角度,等价类划分可定义为一个将输入域映射到等价类标识符集合的划分函数,须满足非空、不相交和完备三个条件。非空保证每个等价类至少有一个元素,不相交保证任意两个等价类交集为空,完备保证所有等价类并集等于完整输入域。这三个条件确保不会出现某输入值既属 A 类又属 B 类的模糊情形。
在实践中,等价类的划分依赖测试人员对程序规格的深入理解。一个常见的划分策略是按输入条件拆解:首先列出规格说明中的所有输入条件,然后对每个条件分别划分有效等价类和无效等价类。例如,一个程序要求输入的金额为正整数且不超过一万,则金额输入条件可拆解为两个维度——数值类型和数值范围。数值类型维度的有效等价类是正整数,无效等价类包括小数、负数、零、非数字字符;数值范围维度的有效等价类是一到一万之间的整数,无效等价类包括大于一万的整数。
边界值分析的工作机制更为具体。以一个允许输入一到一百的程序为例,边界值分析的测试用例应当包括:一、二、零、九十九、一百、一百零一。其中一和一百是边界值本身,零是下边界减一,二和九十九是边界加一或减一,一百零一是上边界加一。这个选值策略并非随意为之,而是基于对程序错误分布的实证研究。大量工程统计表明,程序员在处理循环边界、数组下标、分支条件时,最容易犯的就是"差一错误"——该用小于等于的时候用了小于,该用大于的时候用了大于等于。
边界值分析还衍生出一个重要变体:健壮性边界值测试。健壮性测试在边界值分析的基础上进一步扩展,不仅测试边界值本身和边界值加减一的点,还测试边界值加减典型步长的点,以及超出有效范围较多的点。例如对于一到一百的输入,健壮性测试除了测试一、零、二之外,还会测试负十和二百这样的极端值,以验证程序在遇到严重异常输入时是否能优雅降级而非崩溃。
等价类划分在实际应用中有多种可供选择的划分策略,不同的划分策略适用于不同的测试场景,选择恰当的划分策略直接影响测试效率。
按输入条件划分是最基础也最常用的策略。测试人员逐条审阅需求规格说明,提取每一个显式输入约束,然后对每个约束分别构建有效等价类和无效等价类。以注册表单为例,用户名要求四到十六个字符且仅包含字母数字和下划线,密码要求八到二十个字符且必须包含大小写字母和数字——仅这两个字段就已经产生数十个等价类。按输入条件划分的优点在于规则明确、不易遗漏,缺点在于当输入条件之间存在组合约束时,独立划分可能产生冗余的等价类。
按输出条件划分是从结果反推输入的策略。有些程序输入域复杂、输入条件难以穷举,但输出域结构简单。此时可从输出域出发,将产生相同输出的输入值归为等价类。例如成绩等级评定程序按优秀、良好、中等、及格和不及格五个等级,将输入域对应划分为五个等价类。按输出条件划分的优势在于等价类边界天然与程序功能对齐,劣势在于输出域复杂时反推难度指数级增长。
按程序结构划分是基于白盒视角的策略。测试人员分析程序控制流图,将触发相同执行路径的输入值归为等价类。这种策略精度最高——等价类直接对应程序实际执行逻辑——但成本也最高,要求测试人员深入理解程序内部实现。在软件评测师考试中,按输入条件和输出条件是主要考察方向,按程序结构划分通常出现在白盒测试题目中。
边界值选取遵循一套标准化规则。标准边界值分析选取六个测试点:下边界减一、下边界、下边界加一、上边界减一、上边界、上边界加一。对于多变量函数,可扩展为组合边界值测试——对每个变量边界点进行正交组合。以有两个变量的函数为例,组合边界值测试需生成十三到二十五个测试用例,取决于是否区分有效范围内组合和包含无效值的组合。
等价类划分与边界值分析的协同使用遵循自然工作流:先用等价类划分将输入域分解,确保覆盖面;然后针对等价类边界区域应用边界值分析,确保精度;最后将二者的测试用例去重合并。这个工作流既保证测试系统性又突出针对性。
不同软件类型对测试策略的需求差异很大,等价类划分和边界值分析也需要根据应用场景进行适配。
在 Web 应用测试中,等价类划分的重点是表单验证和用户交互
本篇完!