C/C++单元测试工具Visual Unit 4在线帮助


几种用例设计方法介绍

用于语句覆盖的基路径法
    基路径法保证设计出的测试用例,使程序的每一个可执行语句至少执行一次,即实现语句覆盖。基路径法是理论与应用脱节的典型,基本上没有应用价值,这里不作详细介绍。

用于MC/DC的真值表法
    真值表法用于设计MC/DC的用例,先将条件值的所有可能组合列出表格,然后从中选择用例。与基路径法一样,真值表法成本太高,不具有实践上的使用价值,这里不作详细介绍。

边界值法
    边界值法假定错误最有可能出现在区间之间的边界,一般对边界值本身,及边界值的两边都需设计测试用例。边界值其实是等价类的一部分,没有必要专门使用边界值法。
    如下函数:
    //参数age表示年龄
    int func(int age)
    {
        int ret = 0;
        //… do something
        return ret;
    }

    参数age表示一个人的年龄,假设有效的取值范围是0-200,那么,用边界值法可以得出以下用例(省略输出):
    用例1:age = -1;
    用例2:age = 0;
    用例3:age = 1;
    用例4:age = 199;
    用例5:age = 200;
    用例6:age = 201;

    通常,程序对输入还会分段处理,例如,年龄在10以下,为儿童,需要特别照顾;年龄在60岁以上,为退休老人,不能安排工作,那么,10和60是内部边界,也要设计测试用例:
    用例7:age =9;
    用例8:age = 10;
    用例9:age = 11;
    用例10:age = 59;
    用例11:age = 60;
    用例12:age = 61;

等价类法
    先从代码编写的思路说起。程序员编写一个函数的代码,会如何做呢?

    首先,了解代码功能。程序的功能是什么?无非就是:有哪些输入?执行什么操作或计算?产生什么输出?

    然后,将功能细化,形成一个或多个功能点。一个功能点就是一类输入及其处理。什么叫“一类”输入?程序可能有无数输入,但代码并不需要用无数个判定来对每个输入分别做处理,只需将输入分类,需要做相同处理的输入归于一类,这就是“等价类”。从编程角度来说,“等价类”是指计算或操作过程的“等价”,一个等价类就是处理过程完全相同的输入的集合。程序中通常用判定来识别分类,一个判定就是一次分类,嵌套的判定则会造成分类数量的翻番。

    所以,函数代码编写的核心思维就是等价类划分和处理。一个函数要完全正确,关键是等价类的划分要正确完整,且每个等价类的处理正确。
    举个例子,现在要编写一个函数,将字符串左边的空格删除。函数原形如下:
    char* strtrml(char *str);
    功能:
    将str左边空格删除,并返回str本身。
    功能点:
    1. 左边有空格:删除;(正常输入)
    2. 左边无空格:不作处理;(正常输入)
    3. 全部是空格:全部删除;(正常输入)
    4. 空串:不作处理;(边界输入)
    5. 空指针:直接返回。(非法输入)

    不一定需要针对每个功能点分别写代码,因为程序中的if、for、while等语句本身具有“如果不符合条件就跳过”的含义,所以很多功能点是可以共用代码的,例如,前4个功能点只需要相同的代码,不过,编程时对功能点的考虑还是要全面。

    既然函数没有错误的关键是等价类划分正确完整且处理正确,那么测试时,只要把输入的等价类都列出来,并设定正确的预期输出,进行测试就行了。
   
    这就是通常说的“等价类”法,从测试角度来说的“等价”,是指测试效果上的等价,即同类中一个数据测试通过,可以肯定其他数据也会测试通过。
    用例设计的首要工作是设定输入。输入有哪些呢?要从正常输入、边界输入、非法输入三方面考虑,每方面进一步划分形成等价类,即要考虑:有哪些正常输入?有哪些边界输入?有哪些非法输入?

    多个输入时,例如有多个参数,首先把各个参数的等价类列出来,然后要考虑参数之间是否存在特殊的组合关系。例如下面的函数:
    //计算某年某月某日是星期几,参数分别表示年月日
    int Date(int year, int month, int day);
    用例如下表(假设year的有效范围是1-9999):
 
输入 正常值 边界值 非法值 组合
Year 2000(闰年)
2009(非闰年)
1
9999
  闰年和非闰年要保证都和2月组合;
2月要和28、29、30日组合;
小月要和30、31日组合;
大月要和31、32日组合
month 2(短月)
3(大月)
4(小月)
1
12
 
0
13
Day 10(普通)
28(非闰年二月)
29(闰年二月)
30(小月)
1
31
0
32

    用例的输出是比较容易被轻视的工作,但是,没有充分且正确的预期输出,用例基本上没有意义,就像医生要求病人做一大堆检查,却不看检查结果一样。预期输出要根据程序的设计功能确定正确的值。一个用例的预期输出可能有多个。

    等价类法是与程序的基本特性“对数据分类处理”相匹配的方法。对于一个函数来说,如果对数据的分类正确且完整,每一个分类处理正确,那么,程序就没有问题。同样,测试时,只要依据设计功能,找出所有等价类,那么,用例就是完整的。所以,用例的完整性,本质上是指等价类是否划分正确且完整,每一类的正确输出是否均依据设计功能正确设定。

    使用了等价类法后,是否需要使用其他方法呢?

    等价类法从“有哪些正常输入?有哪些边界输入?有哪些非法输入?”三个方面来考虑等价类,因此,边界值法是等价类法的一部分。

    常见的用例设计方法中还有正交法和错误推测法。正交法考虑数据的组合,实际上,如果程序对输入数据的组合需要判断处理,也是一种等价类划分,但正交法会产生大量的多余组合,且可能缺少必要的组合,因此不推荐采用正交法,应该根据数据的实际意义自行组合。单独从错误推测角度去设计用例未免太不可靠,但错误推测法可以作为检查等价类是否完整的一种思路,即用等价类法设计用例后,可以考虑哪些输入比较容易产生错误,以检查是否遗漏,这只是一种检查思路,也包含在等价类法之中。总之,用例设计只需使用等价类法,但可以从多种角度检查等价类的完整性。

    现在的问题是:如何衡量等价类是否完整?如何找出遗漏的等价类?请参考实现完整测试