第2章 基于结构的测试

基于结构的测试(又称为“白盒测试”)是基于测试对象的代码、数据或者系统架构而进行测试的一种技术,它关注的是测试对象的内部结构。基于结构的测试技术的共同特点如下。

(1)测试对象的内部结构信息是设计测试用例的依据,如程序代码和设计架构。

(2)测试对象的覆盖率可以通过已有的测试用例测量,并且可以系统地增加测试用例来提高覆盖率。

满足基于结构的测试的相关测试准则并不意味着整个测试已完成,而只意味着测试对象不需要基于此技术再执行额外的测试,但是可以继续应用其他测试技术。一些代码覆盖工具可以用来获取基于结构的测试覆盖率,如果测试过程中发现重要的结构覆盖率出现偏差,或要求的覆盖率没有达到,可以使用基于结构或者其他技术增加测试用例以弥补其中的差距。

基于结构的测试技术通常要求测试人员详细了解测试对象的内部结构,如代码的结构,因此测试人员需要具备一定的编程技能。基于结构的测试通常由以下步骤组成。

(1)分析测试对象的具体实现和内部结构。

(2)识别测试对象的不同路径(选择合适的代码覆盖标准,如语句覆盖)。

(3)选择合适的输入数据覆盖测试对象的相关路径并确定期望结果。

(4)执行测试用例。

(5)比较测试对象的实际结果和期望结果。

(6)确定测试对象是否实现了正确的功能。

基于结构的测试技术特别适用于低级别的测试,如组件测试。但是同样也适用于更高级别的测试,如在集成测试过程中考虑的结构是集成模块之间调用的树形结构,以及在系统测试过程中测试对象的菜单结构、网页的关联结构和业务流程等。通过基于结构的测试,测试人员可以确保识别并测试了测试对象的各种路径。基于结构的测试也存在一些缺点,如测试对象需要测试的路径与基于规格说明测试中的输入数据组合一样可能是一个天文数字,对这些路径进行完全的测试基本是不可能的。因此需要采取合适的测试技术,以满足特定测试对象的测试覆盖率要求。

本章中基于结构的测试技术主要基于测试对象的控制流展开,即根据测试对象的控制流测试人员按照不同的覆盖率要求识别测试对象的执行路径,并创建和执行测试用例以覆盖这些路径。为了使读者更好地理解基于结构的测试技术,首先简单描述控制流和控制流图等几个概念。

控制流指的是测试对象中一系列顺序发生的事件或者路径,而控制流图指的是在测试对象执行过程中所有可能的事件或者路径序列的抽象表示。控制流图是控制流测试的基础,描绘了测试对象的控制结构。在基于结构的测试过程中首先将测试对象的代码转换为相应的控制流图,然后分析其中的路径,并根据分析的结果创建相应的测试用例。控制流图有过程块(Process Blocks)、决策点(Decision Point)和汇聚点(Junction Point)等不同元素组成。

(1)过程块。过程块的定义是从开始到结束按照顺序执行的一系列语句,除了在开始处没有其他入口可以进入;同样除了在结束处没有其他出口可以离开过程块。一旦过程块被触发,其中的每个语句都会按照顺序执行。过程块在控制流图中以一个圆圈、一个入口箭头和出口箭头表示,如图2-1所示。

图2-1 过程块

(2)决策点。决策点指的是测试对象模块中的一个点,在这个点上控制流选择的方向发生变化。一般的决策点以二进制形式存在,并通过if-then-else语句实现;多条路径的决策点一般通过case语句实现。决策点以一个圆圈、一个入口点和多个出口点表示,如图2-2所示。

图2-2 决策点

(3)汇聚点。汇聚点指的也是测试对象模块中的一个点,与决策点相反,在汇聚点上不同控制流的选择方向在此汇聚,如图2-3所示。

图2-3 汇聚点

例2-1程序代码到控制流图的转换

下面是某个功能模块中的某段程序代码。

    1. pos_sum(a, num_of_entries, sum)
    2.     sum = 0
    3.     int i = 1
    4.     while (i < = num_of_entries)
    5.             if a[i] >0
    6.                     sum = sum + a[i]
                endif
    7.             i = i + 1
          end while
    8. end pos_sum

根据上面描述的控制流图的组成元素,生成该段程序代码相应的控制流图,如图2-4所示。

图2-4 生成的控制流图

基于结构的测试也存在如下缺点。

(1)测试对象中的控制流路径将非常庞大,在有限的时间内穷尽测试几乎是不可能的。测试对象代码中的任何一个决策都会使路径数目至少增加一倍,而任何一个循环都将相应地增加测试对象路径的数目。看下例:

    for (i = 1; i <= 1000; i++)
          for (j = 1; j <= 1000; j++)
                for (k = 1; k <= 1000; k++)
                      Sum (i,j,k);

(2)根据测试对象的规格说明所设计的代码可能在模块中遗漏了某些路径,而控制流测试依据测试对象实现的路径展开,所以无法发现遗漏的路径。看下例:

    if (a>0) Larger();
    if (a==0) Equal();
    // 遗漏语句 - if (a<0) Less();

(3)尽管测试对象的控制流本身是正确的,但是缺陷仍可能存在于测试对象的语句中,看下例:

    //实际代码(但是不正确)
    m = m -1;
    // 正确的代码
    m = m + 1;

(4)测试对象可能对大部分数据可以正确执行,但是对于小部分数据则无法正确执行。例如,在下面的程序代码中当参数n取值为0,模块执行时就会发生错误:

    int m;
    int n;
    return m/n;

尽管基于结构的测试技术存在这些方面的问题,但并不影响它成为测试人员的有效工具。这种技术主要包括语句测试(Statement testing)、判定测试(Decision testing)、条件测试(Condition testing)、判定条件测试(Decision condition testing)、条件决定测试(Condition determination testing)、条件组合测试(Multiple condition testing)、线性代码序列和跳转测试(Linear Code Sequence And Jump Testing,缩写为LCSAJT)和路径测试(Path testing)。