在软件单元测试时,考虑到软件单元的所有情况有时是非常困难的。能不能采取一些有效的办法,能够在可接受的单元测试用例数量的前提下,提高测试的有效性呢?本文对常用的单元测试用例设计方法进行介绍,期待对大家有一定的借鉴作用。
1、等价类划分(Equivalence Class Partitioning)
等价类划分法将程序所有可能的输入数据和输出(有效的和无效的)划分成若干个等价类,然后从每个等价类中选取具有代表性的数据作为测试用例,从而保证测试用例具有完整性和代表性。
形成测试区间的数据不只是函数/过程的参数,也可以是软件可以访问的全局变量,系统资源等,这些变量或资源可以是以数值,也可能是以其他形式存在,如状态。
例1:计算绝对值的函数的设计说明如下:
工作笔记-单元测试用例设计方法
根据输入和输出,可以划分出如下的2个输入的等价类和一个输出的等价类:
工作笔记-单元测试用例设计方法针对该例子,我们可以设计以下2个测试用例:
1)Test Case1:输入4,返回4,覆盖 I1和O1
2)Test Case2:输入-10,返回10,覆盖I2和O1
2、边界值分析(Boundary Value Analysis)
通常大量的错误发生在输入或输出范围的边界上,而不是发生在输入输出范围的内部。
边界值分析法是对输入或输出的边界值进行测试的一种方法,通常边界值分析法是作为对等价类划分法的补充,此时的测试用例通常来自等价类的边界。
如例1的绝对值的例子中:
工作笔记-单元测试用例设计方法
根据边界值分析方法得出的测试用例有:
Test Case1 :输入0,返回0
Test Case2 :输入仅比0大的数,返回输入的正数
Test Case3 :输入最大正实数,返回输入的正实数
Test Case4 :输入仅比0小的数,返回0-输入的值
Test Case5 :输入最小的负实数,返回0-输入的值
3、基本路径测试(Basis Path Testing)
基本路径测试法是在程序控制流图的基础上,通过分析控制构造的圈复杂度,导出基本可执行路径集合,从而设计测试用例的方法。设计出的测试用例能够保证程序的每个可执行语句至少执行一次。这种单元测试用例的方法首先要创建出程序的控制流图,之后确定程序的圈复杂度,最后进行测试用例的设计。
3.1 创建出程序的控制流图
程序的控制流程图是描述程序控制流的一种图示方法,与程序流程图类似。程序控制流图只有圆形和箭头两种图形符号。圆形称为控制流图的一个结点,表示一个或多个无分支的语句或源程序语句;箭头称为边或连接,代表控制流。需要注意的是,在创建程序的控制流图时,如果判断中的条件表达式是由一个或多个逻辑运算符(OR, AND)连接的复合条件表达式,则需要改为一系列只有单条件的嵌套的判断。
如下面代码的控制流图:
工作笔记-单元测试用例设计方法
3.2 计算圈复杂度
圈复杂度是一种度量程序逻辑复杂性的软件度量指标,是程序的基本的独立路径数目,即为确保所有语句至少执行一次的测试数量。根据程序控制流图有以下三种计算方法:
工作笔记-单元测试用例设计方法a) 控制流图中封闭区域的数量,其中图所在的平面认为是一个区域
上图的圈复杂度为3 (区域中的蓝色部分)
b) 控制流图中边的数量减去结点数量+2
V(G) = E – N + 2
上图中,圈复杂度为 : 7-6+2 = 3
c) 控制流图中判定结点的数量+1
V(G) = P + 1
其中P为控制流图中判定结点的数量
上图判定结点为2个,因此圈复杂度为3
3.3 编写测试用例
根据圈复杂度计算结果和程序控制流图,得出独立路径数和相应的独立路径。根据独立的路径,分别设计输入条件数据,使程序分别执行到所有独立路径,即可得到相应的测试用例。
如之前的数据流图,根据圈复杂度,得出有3个独立的路径,根据相应的独立路径,分别设计输入条件,则可以确定3个测试用例:
工作笔记-单元测试用例设计方法Test Case1 :
执行路径: 1-2-6
输入条件: C1 = 11 ; C2任意
Test Case2 :
执行路径: 1-3-4-6
输入条件: C1 = 8, C2 = 8
Test Case3 :
执行路径: 1-3-5-6
输入条件: C1 = 8, C2 = 12
4、基于测试覆盖度的测试用例设计
单元测试中,经常还提到一个测试覆盖度的概念,比如语句覆盖、分支覆盖等,也是一种单元测试用例的设计方法。具体可以参见杨老师的文章:谈谈测试覆盖度,在此不在赘述。
5、软件设计说明导出的测试(Specification derived test)
我们按照以上所有的方法都进行了单元测试用例,我们的代码就没有问题了吗?我们看下图的例子。
工作笔记-单元测试用例设计方法我们应用了所有上面提及的单元用例设计方法,但是可能仍然不能发现例子中的除以0的问题。因为我们之前的测试用例设计方法更多关注的是程序中的判断表达式及表达式中的条件,即是否所有代码都执行过,而不是所有代码都正确的执行。所以,我们通常说的代码覆盖度实际上也被称为代码的结构覆盖度(Structural coverage),而不是需求逻辑的覆盖。
因此,我们还需要考虑从需求/规格方面考虑的用例设计方法。对于单元测试来说,规格就是详细设计。
软件设计说明导出的测试用例,就是通过根据相关的软件设计说明文档进行设计,每个测试用例测试设计说明中一项或多项陈述。
如例1中,设计说明中有2个陈述,可以用以下2个测试用例来对应。
Test Case 1:输入4,返回4。 // 对应第一个陈述
Test Case 2:输入-10,返回10 // 对应第二个陈述
例1:计算数的绝对值的函数的设计说明如下:
工作笔记-单元测试用例设计方法
6、错误猜测 (Error Guessing)
错误猜测法就是根据经验猜想可能的错误,并依此设计测试用例的方法。错误猜测法只能作为测试设计的补充而不能单独用来设计测试用例,否则可能会造成测试的不充分。
错误猜测法的基本思路:
列举出程序中所有可能有的错误和容易发生错误的特殊情况,根据他们选择测试用例,例如在单元测试时许多在模块中常见的错误或以前产品测试中曾经发现的错误等。
如之前的被除数为零的例子的情况。测试人员会根据算法中有除法运算,那么就可以推测出,至少需要考虑是否存在被除数为零的情况。
虽然说了这么多的测试用例设计方法,但这并不是所有。针对测试内容的不同,可能还会有一些测试用例设计方法,如状态转换测试(State-transition Testing)等,这些测试用例设计方法适用于不同的情况下的测试,因此在这里不在介绍。
我们给出了这么多的测试用例设计方法,实际使用时,需要根据自己项目的情况选用恰当的方法进行测试用例的开发。实际上,组合使用各种测试用例设计方法时,所设计出的测试用例是存在重复的情况,因此我们在进行单元测试用例设计时,还要恰当的去重。那么,整个单元测试用例设计的过程到底是什么样的呢?通常的流程如下,供大家参考:
1) 确定单元测试的策略
2) 设计正面测试(Positive Testing)测试用例
3) 设计负面测试(Negative Testing)测试用例
4) 设计覆盖率测试用例,使测试用例满足预定的覆盖度要求
5) 设计需求中其他测试特性测试用例
6) 最后在测试执行后,可能会需要对测试用例进行调整、补充,如测试覆盖度没有达到预期目标时
网友评论