《软件测试设计》第2章——基于结构的测试
Posted st追杀者
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《软件测试设计》第2章——基于结构的测试相关的知识,希望对你有一定的参考价值。
概念:又称白盒测试,是基于测试对象的代码、数据或者系统架构而进行测试的一种技术
关注测试对象的内部结构
基于结构的测试技术的共同特点为:
① 测试对象的内部结构信息是设计测试用例的依据,如程序代码和设计架构
② 测试对象的覆盖率可通过已有的测试用例测量,并且可系统地增加测试用例来提高覆盖率
要求:测试人员需详细了解测试对象的内部结构
步骤:
① 分析测试对象的具体实现和内部结构
② 识别测试对象的不同路径(选择合适的代码覆盖标准,如语句覆盖)
③ 选择合适的输入数据覆盖测试对象的相关路径并确定期望结果
④ 执行测试用例
⑤ 比较测试对象的实际结果和期望结果
⑥ 确定测试对象是否实现了正确的功能
应用:
① 特别适用于低级别测试,如组件测试
② 适用于更高级别测试,如集成测试中考虑的结构是集成模块间调用的树形结构。以及在系统测试过程中测试对象的菜单结构、网页的关联结构和业务流程等。
作用:测试人员可确保识别并测试了测试对象的各种路径
缺点:若路径和输入数据的组合很多,对这些路径进行完全的测试基本不可能,需采取合适的测试技术以满足覆盖率要求。
下引入控制流和控制流图等几个概念
控制流:测试对象中一系列顺序发生的事件或路径
控制流图:在测试对象那个执行过程中所有可能的事件或路径序列的抽象表示
控制流图是控制流测试的基础。
基于结构的测试中首先将测试对象的代码转换为相应控制流图,然后分析其中的路径,并根据分析的结果创建相应的测试用例。
控制流图组成
① 过程块
② 决策点
③ 汇聚点
控制流图缺点:
① 路径庞大时有限时间内穷尽测试难以实现
② 根据规格说明所设计的代码可能在模块中遗漏某些路径,而控制流测试依据测试对象实现的路径展开,所以无法发现遗漏的路径。
③ 测试对象对小部分数据可能无法正确执行
下介绍基于结构的各种测试技术
2.1 语句测试
概念:设计若干测试用例来执行程序代码中的语句
语句覆盖指被执行的语句数与所有可能的语句数之间的比值
案例1
if(a>0&&b>0){c=c/a;} if(a>1||c>1){c=c+1;} c=c+b;
化成控制流图
满足100%语句覆盖准则所对应的测试用例
测试用例ID | 输入a、b和c | 输出a、b和c | 满足的判定 | 覆盖的路径 |
1 | a=1、b=2且c=3 | a=1、b=2且c=6 | T1T2 | abdegh |
其它测试用例
测试用例ID | 输入a、b和c | 输出a、b和c | 满足的判定 | 覆盖的路径 |
2 | a=1、b=2且c=-3 | a=1、b=2且c=-1 | T1F2 | abdfh |
3 | a=-1、b=2且c=3 | a=-1、b=2且c=6 | F1T2 | acegh |
尽管语句测试可识别没有测试到的代码块,但可能无法正确判断程序代码中的逻辑关系
例:案例1中语句测试无法发现(a>0&&b>0)错写为(a>0||b>0)
语句覆盖可能会遗漏多个路径(本案例共4条路径)
尽管语句覆盖是最低级别的测试覆盖,在实际测试中达到100%的语句覆盖也不易
2.2 判定测试
概念:是种针对判定结果设计测试用例的技术。
判定覆盖:执行测试套件能够覆盖的判定结果百分比
低级别测试中,判定覆盖可作为出口准则之一。例:测试出口准则可要求测试对象达到100%的判定覆盖,100%的判定覆盖可保证100%的语句覆盖。
案例1中(T1 T2)和(F1 F2)两个用例满足100%判定覆盖
测试用例ID | 输入a、b和c | 输出a、b和c | 满足的判定 | 覆盖的路径 |
1 | a=1、b=2且c=3 | a=1、b=2且c=6 | T1T2 | abdegh |
2 | a=-1、b=2且c=-3 | a=-1、b=2且c=-1 | F1F2 | acfh |
或者用(T1 F2)和(F1 T2)两个用例满足100%判定覆盖
测试用例ID | 输入a、b和c | 输出a、b和c | 满足的判定 | 覆盖的路径 |
3 | a=1、b=2且c=-3 | a=-1、b=2且c=-1 | T1F2 | abdfh |
4 | a=-1、b=2且c=3 | a=-1、b=2且c=6 | F1T2 | acegh |
可以看出,100%的判定覆盖可保证100%的语句覆盖。
判定测试可发现程序中的逻辑错误,但并不能保证发现判定中原子条件的错误
2.3 条件测试
概念:设计若干测试用例来执行不同条件的结果
条件覆盖指被执行条件能够覆盖原子条件的百分比,需要注意条件覆盖并不比判定覆盖更强。
举例:黑体T和F表示判定的真假,普通T和F表示判定中条件的真假。
原子条件 | a>0 | b>0 |
a>1 |
c>1 |
取真 | T1 | T1 | T3 | T4 |
取假 | F1 | F2 | F3 | F4 |
可得原子条件的所有组合及满足条件的取值
若要测试对象满足100%的条件覆盖,那么根据定义需将判定中的每个原子取值分别取真和假一次。
举例
测试用例ID | 输入a、b和c | 输出a、b和c | 满足的判定 | 满足的条件 | 覆盖的路径 |
1 | a=2、b=1且c=6 | a=2、b=1且c=5 | T1T2 | T1T2T3T4 | abdegh |
2 | a=-1、b=-1且c=-2 | a=-1、b=-1且c=-3 | F1F2 | F1F2F3F4 | acfh |
上例中100%的条件覆盖也满足了100%的判定覆盖,但条件覆盖不保证100%判定覆盖,举例:
测试用例ID | 输入a、b和c | 输出a、b和c | 满足的判定 | 满足的条件 | 覆盖的路径 |
3 | a=2、b=1且c=-2 | a=2、b=1且c=1 | T1T2 | T1T2T3F4 | abdegh |
4 | a=-1、b=-1且c=6 | a=-1、b=-1且c=6 | F1T2 | F1F2F3T4 | acgeh |
所以,条件覆盖并不比判定覆盖更强。
条件覆盖也可能无法发现测试对象中的逻辑错误
2.4 判定条件测试
概念:指设计若干测试用例来执行条件结果和判定结果
判定条件覆盖指执行测试用例套件能够覆盖的条件结果和判定结果百分比
100%判定条件覆盖意味着100%的判定覆盖和100%的条件覆盖
例
测试用例ID | 输入a、b和c | 输出a、b和c | 满足的判定 | 满足的条件 | 覆盖的路径 |
1 | a=2、b=1且c=6 | a=2、b=1且c=5 | T1T2 | T1T2T3T4 | abdegh |
2 | a=-1、b=-1且c=-2 | a=-1、b=-1且c=-3 | F1F2 | F1F2F3F4 | acfh |
满足100%判定条件覆盖并不唯一
测试用例ID | 输入a、b和c | 输出a、b和c | 满足的判定 | 满足的条件 | 覆盖的路径 |
3 | a=2、b=1且c=-2 | a=2、b=1且c=1 | T1T2 | T1T2T3F4 | abdegh |
4 | a=-1、b=-1且c=6 | a=-1、b=-1且c=6 | F1T2 | F1F2F3T4 | acegh |
5 | a=0.5、b=1且c=0.5 | a=0.5、b=1且c=2 | T1F2 | T1T2F3F4 | abdfh |
2.5 条件决定测试
能够独立影响判定结果的单独条件测试(测试对象中每个条件必须产生所有可能的输出结果至少一次,并且每个判定中的每一个条件必须能够独立影响该判定的输出。即在其它条件不变的前提下仅改变这个条件的值,可使判定结果发送改变)
条件决定覆盖指的是执行测试套件覆盖到能独立影响判定结果的单个条件百分比
100%条件决定覆盖意味着100%的判定条件覆盖
案例2
if(a>0&&(b>0||c<1)){c=b/a;} c=a+b+c;
可得控制流图
黑体T和F表示判定的真假,普通T和F表示判定中条件的真假。
可得原子条件的所有组合及满足条件的取值
根据定义,满足100%的判定条件覆盖只要选择ID为2和7,即可得到两个测试用例
测试用例ID | 输入a、b和c | 输出a、b和c | 满足的判定 | 满足的条件 | 覆盖的路径 |
1 | a=2、b=4且c=2 | a=2、b=4且c=2 | T1 | T1T2F3 | abde |
2 | a=-1、b=-1且c=-2 | a=-1、b=-1且c=-3 | F1 | F1F2T3 | ace |
若要条件决定测试实现100%的条件覆盖,选择ID2,3,4,7
其中:
2和4,a、c保持不变时,b可以完成T1和F1的切换
3和4,a、b保持不变时,c可以完成T1和F1的切换
3和7,b、c保持不变时,a可以完成T1和F1的切换
测试用例如下表
测试用例ID | 输入a、b和c | 输出a、b和c | 满足的判定 | 满足的条件 | 覆盖的路径 |
1 | a=2、b=4且c=2 | a=2、b=4且c=2 | T1 | T1T2F3 | abde |
2 | a=2、b=-4且c=-1 | a=2、b=-4且c=-2 | T1 | T1F2T3 | abde |
3 | a=2、b=-4且c=2 | a=2、b=-4且c=0 | F1 | T1F2F3 | ace |
4 | a=-1、b=-1且c=-2 | a=-1、b=-1且c=-3 | F1 | F1F2T3 | ace |
可看出,100%条件决定覆盖意味着100%的判定条件覆盖
对于判定条件测试而言,无论判定中的原子条件个数有多少,满足100%判定条件覆盖的测试用例数目最少可以是两个。
而对于条件测试而言,若一个判定中有N个原子条件,每个原子条件都出现一次(保持其它原子条件不变,并且这个原子取值的变化会导致判定结果的变化),那么要满足100%的条件决定覆盖需要N+1个原子条件的组合,即需要N+1个测试用例。
所以条件决定测试方法比判定条件测试方法具有更高测试覆盖率,且满足100%的条件决定覆盖的测试用例数目与判定中的原子条件个数有关。
2.6 条件组合测试
概念:指测试用例覆盖每条语句中原子条件所有可能的取值结果组合(即每个判定中的所有可能的原子条件取值至少执行一次)
条件组合覆盖指测试条件覆盖每条语句内所有原子条件取值结果组合的百分比。
100%条件组合覆盖意味着100条件决定覆盖
以案例1为例,根据条件组合定义,得到第一个判定和第二个判定的所有原子组合。
第1个判定所有原子条件组合取值的组合
第2个判定所有原子条件组合取值的组合
合并两个表,即达到100%条件组合覆盖,如下
测试用例ID | 输入a、b和c | 输出a、b和c | 满足的判定 | 满足的条件 | 覆盖的路径 |
1 | a=2、b=1且c=6 | a=2、b=1且c=5 | T1T2 | T1T2T3T4 | abdegh |
2 | a=2、b=-4且c=-4 | a=2、b=-4且c=-7 | F1T2 | T1F2T3F4 | acegh |
3 | a=-2、b=-4且c=4 | a=2、b=-4且c=1 | F1T2 | F1T2F3T4 | acegh |
4 | a=-1、b=-1且c=-2 | a=-1、b=-1且c=-3 | F1F2 | F1F2F3F4 | acfh |
2.7 线性代码序列和跳转测试
概念:主要针对LCSAJ覆盖来进行测试用例设计
LCSAJ覆盖:指测试条件执行的LCSAJ数目占总LCSAJ的百分比
100%LCSAJ覆盖率意味着100%的判定覆盖
LCSAJ指软件代码路径的片段,由控制流跳转跟着的序列代码构成,主要由下面3部分构成(通常通过源代码的行号来识别)
① 可执行语句线性序列的起始点
② 可执行语句线性序列的结束点
③ 在线性序列结尾控制流所转移到的目标行
LCSAJ在用于覆盖分析时,其测试方法有如下3个不同的测试有效性比率TER
① 执行语句的百分比TER1:测试条件执行的语句数目与总共可执行语句数目之间的比值
② 执行分支的百分比TER2:测试条件执行的控制流分支数目与总共控制流分支数目之间的比值
③ 执行语句的百分比TER3:测试条件执行的LCSAJ数目与总共LCSAJ数目之间的比值
当测试对象满足TER3=100%时,可同时满足100%的TER1和TER2
案例3
根据上面的代码可得到完整的LCSAJ列表:
若测试覆盖要求100%的TER3时,那么上表中每个LCSAJ都需要遍历一次。
从案例3可知,LCSAJ可以包含一个判定的点,以反映执行LCSAJ所需满足的条件。例如,LCSAJ2包含while语句,其判定条件必须为真
另外每个行码都包含LCSAJ密度。例17行在6个LCSAJ中出现,因此其代码密度为6。
2.8 路径测试
概念:指设计测试用例来执行不同路径
路径覆盖指测试条件执行的路径点占总路径百分比
100%路径覆盖可确保100%的语句覆盖、判定覆盖和LCSAJ覆盖
路径测试的主要步骤:
① 根据源程序得到控制流图
② 计算控制流图的图复杂度C
③ 选择基本路径
④ 为每条基本路径创建一个测试用例
⑤ 执行测试用例
案例4
控制流图为
控制流图的图复杂度C=边数-节点数+2
对于此例,边数24,节点数19,图复杂度C为7
若控制流图中判定都是二元方式(每个节点分化出来的只有两条边),那么图复杂度C的计算更简单:C=p+1 p为二元判定的数目
本例,二节点点数为6个,为A、D、E、F、K和L
图复杂度的含义是测试对象以线性方式组合可得到的独立的和无环回路径(基本路径)的最小数目。
控制流图中任何一条基本路径至少覆盖了其他路径没有覆盖的一个节点。
因为路径测试中的基本路径的集合覆盖了控制流图的所有边和节点,所以满足100%的语句覆盖和判定覆盖。
下逐步得到7条基本路线
① 选择测试对象最频繁执行的路径,这里选ABDEGKMQS
② 仅改变①的第1个判定方向:ACDEGKMQS
③ 改变第2个判定方向:ABDFILORS
④ 改变第3个判定方向:ABDEHKMQS
⑤ 按前面思路直到典型路径到达控制流图的最后一个判定输出,第5条路径为ABDEGKNQS
⑥ 目前基于典型路径的所有判定都已覆盖,下根据②的路径选择不同的判定输出,分别得到ACDFILORS和ACDFILPRS
根据7条路径,为每条路径创建一个测试用例,选择合适测试输入数据进行遍历。
基本路径集合并不一定唯一。
2.9 案例分析
2.9.1 实现各种测试覆盖
实例源代码为
d=1; if(a>5&&(b<2||c>2)){d=a+b+c;} if(a<1||b>5){d=d+c}; d=d-a;
测试的要求和目标:
① 转换为相应控制流图
② 应用语句测试,选择合适测试用例使之达到100%的语句覆盖
③ 应用判定测试,选择合适测试用例使之达到100%的判定覆盖
④ 应用条件测试,选择合适测试用例使之达到100%的条件覆盖
⑤ 应用判定条件测试,选择合适测试用例使之达到100%的判定条件覆盖
⑥ 应用条件决定测试,选择合适测试用例使之达到100%的条件决定覆盖
⑦ 应用条件组合测试,选择合适测试用例使之达到100%的条件组合覆盖
(1) 控制流图
(2) 语句测试
遍历路径abdeg就可以实现100%的语句覆盖
测试用例ID | 输入a、b、c和d | 输出a、b、c和d | 满足的判定 | 覆盖的路径 |
1 | a=6、b=6、c=3且d=1 | a=6、b=6、c=3且d=12 | T1T2 | abdeg |
(3) 判定测试
选择T1T2和F1F2,即分别遍历abdeg和acf
测试用例ID | 输入a、b、c和d | 输出a、b、c和d | 满足的判定 | 覆盖的路径 |
1 | a=6、b=6、c=3且d=1 | a=6、b=6、c=3且d=12 | T1T2 | abdeg |
2 | a=3、b=4、c=2且d=1 | a=3、b=4、c=2且d=-2 | F1F2 | acf |
(4) 条件测试
先获得原子条件的对应关系表
根据对应关系,以及代码中具体语句可的原子条件的所有组合及满足条件的取值,如下:
删去不符合逻辑条件组合,整理出有效的原子条件组合及相应取值
下选择1和8设计测试用例满足100%条件覆盖
测试用例ID |
输入a、b、c和d |
输出a、b、c和d | 满足的判定 | 满足的条件 | 覆盖的路径 |
1 | a=6、b=1、c=6且d=1 | a=6、b=1、c=6且d=7 | T1F2 | T1T2T3F4F5 | abdf |
2 | a=-1、b=6、c=1且d=1 | a=-1、b=6、c=1且d=3 | F1T2 | F1F2F3T4T5 | aceg |
亦可以选择2和7,3和6,4和5这些组合。
(5) 判定条件测试
采用上述1和8或2和7都可以实现。
(6) 条件决定测试
100%的条件决定覆盖是指,测试对象中的每个条件必须产生所以可能的输出结果至少一次,并且每个判定中的每一个条件必须能够独立影响一个判定的输出(我觉得这个定义有问题),即在其他条件不变的前提下仅改变这个条件的值就可以使判定结果发生改变。
对第1个判定,满足条件决定的原子条件组合和取值如下
其中,1和5是对第一个判定的第1个条件。2和4是对第一个判定的第2个条件。3和4是对第一个判定的第一个条件。
对第2个判定,满足条件决定的原子条件组合和取值如下
其中,10和12是对第二个判定的第1个条件。11和12是对第二个判定的第2个条件。
将满足第1个判定的原子条件组合和第2个判定的原子组合合并,可得如下测试用例和测试数据
测试用例ID | 输入a、b、c和d | 输出a、b、c和d | 条件组合 | 满足的判定 | 覆盖的路径 |
1 | a=6、b=1、c=6且d=1 | a=6、b=1、c=6且d=7 | T1T2T3F4F5 | T1F2 | abdf |
2 | a=6、b=1、c=1且d=1 | a=6、b=1、c=1且d=2 | T1T2F3F4F5 | T1F2 | abdf |
3 | a=6、b=6、c=6且d=1 | a=6、b=6、c=6且d=18 | T1F2T3F4T5 | T1T2 | abdeg |
4 | a=6、b=6、c=1且d=1 | a=6、b=6、c=1且d=-4 | T1F2F3F4T5 | F1T2 | aceg |
5 | a=-1、b=1、c=6且d=1 | a=-1、b=1、c=6且d=8 | F1T2T3T4F5 | F1T2 | aceg |
(7) 条件组合测试
100%的条件组合覆盖是让每个判定中的所有可能的原子条件组合至少执行一次。首先分析代码中每个判定的所有原子条件的组合。
进行组合,选择符合逻辑的测试用例,如下
测试用例ID | 输入a、b、c和d | 输出a、b、c和d | 条件组合 | 满足的判定 | 覆盖的路径 |
1 | a=6、b=1、c=6且d=1 | a=6、b=1、c=6且d=7 | T1T2T3F4F5 | T1F2 | abdf |
2 | a=6、b=1、c=1且d=1 | a=6、b=1、c=1且d=2 | T1T2F3F4F5 | T1F2 | abdf |
3 | a=6、b=6、c=6且d=1 | a=6、b=6、c=6且d=18 | T1F2T3F4T5 | T1T2 | abdeg |
4 | a=6、b=6、c=1且d=1 | a=6、b=6、c=1且d=-4 | T1F2F3F4T5 | F1T2 | aceg |
5 | a=-1、b=1、c=6且d=1 | a=-1、b=1、c=6且d=8 | F1T2T3T4F5 | F1T2 | aceg |
6 | a=-1、b=1、c=1且d=1 | a=-1、b=1、c=1且d=3 | F1T2F3T4F5 | F1T2 | aceg |
7 | a=-1、b=6、c=6且d=1 | a=-1、b=6、c=6且d=8 | F1F2T3T4T5 | F1T2 | aceg |
8 | a=-1、b=6、c=1且d=1 | a=-1、b=6、c=1且d=3 | F1F2F3T4T5 | F1T2 | aceg |
2.9.2 圈复杂度和路径测试
案例5
Example(){ s1; s2; s3; if(c1){
s4;
s5;
s6;
} else{ s7; s8; } while(c2){ s9; s10; switch(c3){ case-A: s20; s21; s22; break;//End of Case-A case-B: s30; s31; if(c4){ s32; s33; s34; } else{ s35; } break;//End of Case-B case-C: s40; s41; break;//End of Case-C case-D: s50; break;//End of Case-D }//End switch
s60; s61; s62; if(c5){ s70; s71; } s80; s81;
}//End while s90; s91; s92; Result result; }
代码中语句用s1和s2表示,判定条件用c1和c2表示。
(1) 控制流图
(2) 计算圈复杂度C
结果为8(25-19+2=8)
(3) 选择基本路径
化简图如图
根据之前方法,找8条独立路径(个人觉得找够8条就结束,肯定能满足覆盖)
(也就是说,有时候少于8条可能完成语句覆盖,但有8条语句,肯定能满足覆盖)
ABDES
ABCES
ABDEFGHOPRES
ABDEFGILNOPRES
ABDEFGJOPRES
ABDEFGKOPRES
ABDEFGLMOPRES
ABDEFGJOPQRES
(4) 设计测试用例
2.10 小结
不同测试技术的测试覆盖率的强度关系
以上是关于《软件测试设计》第2章——基于结构的测试的主要内容,如果未能解决你的问题,请参考以下文章