购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

5.2 逻辑覆盖

逻辑覆盖(Logic Coverage)是基于程序流程图,计算圈复杂度,确定基本路径集合,即基于“主路径+转向”策略,确定从程序入口开始,到出口结束的路径,以主路径为基础,经过判断节点(包括循环节点),每经过一个未转向判断节点时,在该节点处转向一次,剔除不可行路径,补充执行概率较高、算法复杂、包含严重缺陷、具有高风险等重要路径,根据路径集合设计测试用例,执行测试,遍历程序逻辑结构,实现逻辑结构覆盖。逻辑覆盖是一系列测试过程的总称。这组测试过程正逐渐演变成为完整的通路测试。

基于逻辑驱动覆盖,必须解剖程序逻辑结构。程序结构是一种由软件内部定义的程序执行方式。任何算法都可以由顺序结构、分支结构、循环结构这三种基本结构组合而成。基本程序结构如图5-6所示。

图5-6 基本程序结构

对于顺序结构,仅需设计简单测试用例,遍历每条语句即可,覆盖简单。对于分支和循环结构,路径数和循环次数可能较多,遍历所有路径,存在较大的困难。通常,优先考虑代码覆盖,在测试策划过程中,确定覆盖率目标,如80%,但对于可靠性、安全性攸关软件,则要求覆盖率达到100%。要完整地覆盖程序代码行,就必须做到代码结构的分支覆盖,进一步检验构成分支判断的各个判定条件及其组合,要求实现条件覆盖以及条件组合覆盖。

逻辑覆盖不仅适用于代码检测,也可以覆盖需求层次的业务逻辑,其应用范围可以扩展到业务流程图、数据流图。钱忠胜在《基于规格说明的若干逻辑测试准则》一文中,针对决定性逻辑覆盖准则存在的不足,构建了基于掩盖性逻辑测试准则的测试用例生成算法,从判定的结构入手,分析条件之间的约束关系,复杂判定的分解、合成及判定之间的关系,提出了全真判定覆盖、全假判定覆盖、完全子判定覆盖、唯一条件真覆盖、唯一条件假覆盖等测试准则,为需求层次的业务逻辑覆盖奠定了基础。

5.2.1 语句覆盖

语句覆盖(Statement Coverage)就是使每条可执行语句至少被执行一次。因此,语句覆盖又被称为代码行覆盖(Line Coverage)、段覆盖(Segment Coverage)、基本块覆盖(Basic Block Coverage)。这里,以如下函数为例进行语句覆盖及其他逻辑覆盖讨论。

对于此函数,能够非常容易地得到如图5-7所示的程序流程图。

为了方便说明,对图5-7所示流程图,分别编号如下:

图5-7 函数程序流程图

入口:s。

if((A>1)&&(B==0)):a。

if((A==2)||(X>1)):b。

X=X/A:c。

X=X+1:e。

返回:d。

由图5-7可以直观地看出,该流程图具有如下4条不同路径。

L1:s-a-c-b-e-d。

L2:s-a-b-d。

L3:s-a-b-e-d。

L4:s-a-c-b-d。

对于C语言,一个分号对应于一条语句。判断语句数量时,只需计数其分号个数即可。显然,该函数共有如下5条语句:

float A,B,X;//变量定义语句,一定执行。

scanf(“%f %f %f ”,&A,&B,&X);//输入语句,由用户输入执行。

X=X/A;//赋值语句,不一定执行。

X=X+1;//赋值语句,不一定执行。

printf(“%f ”,X);//打印语句,一定执行。

if((A>1)&&(B==0))和if((A==2)||(X>1))是判定条件,而非语句。

语句覆盖可以直观地基于源代码设计测试用例,不需要细分每一条判定表达式。显然,只需设计并运行测试用例(A=2,B=0,X=2),程序执行路径s-a-c-b-e-d即被覆盖,同时满足条件((A>1)&&(B==0))和((A==2)||(X>1)),即X=X/A和X=X+1两条语句被执行。但该测试用例只能覆盖L1:s-a-c-b-e-d,即两个判断条件都为“真”的情况,并不能覆盖所有分支,即当两个条件都为假时,L2:s-a-b-d未能被覆盖。

由于语句覆盖仅仅针对程序逻辑中显式存在的语句,对于隐藏条件和可能到达的隐式逻辑分支则无法覆盖,且语句覆盖只运行一次,仅覆盖代码中的可执行语句,未考虑各种分支及组合以及分支及循环中的代码,逻辑计算错误等情况,无法全面、有效地反映多分支逻辑运算。也就是说,语句覆盖无法解决路径中的多语句及判定中的逻辑运算错误。相对于路径覆盖而言,语句覆盖是一种最弱的逻辑覆盖。进一步,我们考察如下代码:

显而易见,对于这个仅有一条语句、结构简单的代码,用测试用例(a=2,b=1)即可实现100%的语句覆盖。但该测试用例不能发现当 时,会出现零除异常这一简单错误。

5.2.2 判定覆盖

判定覆盖(Decision Coverage)是使得每个判断取“真”以及“假”的分支至少被执行一次。一个判定代表程序的一个分支,所以说,判定覆盖也称为分支覆盖。

对于图5-7所示函数程序流程图,能够覆盖L1:s-a-c-b-e-d、L2:s-a-b-d、L3:s-a-b-e-d及L4:s-a-c-b-d 4 条路径的测试用例即满足判定覆盖标准。路径L1:s-a-c-b-e-d是条件if((A>1)&&(B==0))及条件if((A==2)||(X>1))为“真”的路径,路径L2:s-a-b-d是条件if((A>1)&&(B==0))及if((A==2)||(X>1))均为“假”的路径,两两组合为一组,覆盖每个条件取“真”“假”的分支;路径L3:s-a-b-e-d是条件if((A>1)&&(B==0))为“真”而条件if((A==2)||(X>1))为“假”的路径,路径L4:s-a-c-b-d是条件if((A>1)&&(B==0))为“假”而条件if((A==2)||(X>1))为“真”的路径,两两组合成一组,覆盖每个条件取“真”和“假”的分支。据此设计覆盖此4条路径的测试用例,如表5-2所示。

表5-2 判定覆盖测试用例表

对于表5-2给出的两组测试用例,任选其一均可实现该函数的判定覆盖。但是,因为大部分判定语句由多个逻辑条件组合而成,在运行过程中往往只判断其最终结果,忽略每个条件的取值情况,可能导致部分测试路径遗漏。

5.2.3 条件覆盖

条件覆盖(Condition Coverage)是使得每个判断的每个条件的所有可能取值至少被执行一次,且每个判定表达式中的每个条件均能够得到可能结果及其结果的组合。

图5-7所给出的函数程序流程图共包括4个条件,每个条件都有“真”“假”两种可能,在 a b 两个点,共产生如下8个条件状态。

(1)在a点:A>1,A≤1,B = 0,B≠0。

(2)在b点:A = 2,A ≠ 2,X >1,X≤1。

据此,设计如下测试用例:

(1)A = 2,B = 1,X = 4(L3:s−a−b−e−d)。

(2)A = −1,B = 0,X = 1(L2:s−a−b−d)。

显然,执行这两组测试用例,即可覆盖此8个条件状态,即A>1,A≤1,B = 0,B ≠ 0以及A = 2,A ≠ 2,X >1,X≤1。

较判定覆盖,条件覆盖增加了对符合判定情况的测试,增加了测试路径,使判定表达式中每个条件都取到“真”“假”两个不同状态。尽管判定覆盖只关心整个判定表达式的值,但条件覆盖仅仅考虑每个条件至少被执行一次,这就可能使得测试用例无法覆盖整个程序的全部分支,如果要覆盖全部分支,则需要足够多的测试用例。条件覆盖并不能保证判定覆盖,上述两组测试用例就未能覆盖判定条件((A>1)&&(B==0))取“真”的分支。条件覆盖只能够保证每个条件至少一次为“真”,而非所有判定结果。

5.2.4 判定−条件覆盖

判定−条件覆盖(Decision-Condition Coverage)是将判定覆盖和条件覆盖综合,使得判定条件中所有条件的可能取值,所有判断的可能结果至少执行一次的测试方法。仍以图5-7所示函数程序流程图为例进行讨论。设计如下测试用例:

(1)A = 2,B = 0,X = 4(L1:s−a−c−b−e−d)。

(2)A = 1,B = 1,X = 1(L2:s−a−b−d)。

此两组测试用例覆盖程序取“真”和“假”判断(L1:s−a−c−b−e−d和L2:s−a−b−d),同时,该测试用例还覆盖了判定表达式中条件取“真”及“假”两种情况,即A>1,A≤1,B = 0,B ≠ 0及A = 2,A ≠ 2,X >1,X≤1两个判断的8种状态。

判定−条件覆盖测试所有条件的所有可能结果,解决了判定覆盖和条件覆盖存在的问题。但是该方法未关注条件组合,有些条件可能掩盖另外一些条件,如“与”表达式中,当某一条件为“假”时,整个判定值为“假”,该判定中的其他条件被掩盖。同样,当“或”表达式中某一条件为“真”时,整个判定值为“真”,该判定中的其他条件被掩盖。判定−条件覆盖可能无法检出判定表达式中的错误。为此,业界研究提出了条件组合覆盖测试技术。

5.2.5 条件组合覆盖

条件组合覆盖(Condition Combination Coverage)是判定表达式中条件的各种可能组合以及每个判定本身的判定结果至少执行一次,覆盖所有条件的所有可能组合。相较条件覆盖,条件组合覆盖并非简单要求每个条件出现“真”与“假”两种判定结果,而是要求这些结果的所有可能组合至少出现一次。

图5-7所示函数包含((A>1)&&(B==0))和((A==2)||(X>1))4个条件。在a点,所有条件两两组合,构成如下条件组合:

(1)A>1,B=0。

(2)A>1,B≠0。

(3)A≤1,B=0。

(4)A≤1,B≠0。

在b点,所有条件两两组合,构成如下条件组合:

(1)A=2,X>1。

(2)A=2,X≤1。

(3)A≠2,X>1。

(4)A≠2,X≤1。

在a、b两点各有4组共8种可能的条件组合。据此设计如下测试用例:

(1)A=2,B=0,X=4(L1:s−a−c−b−e−d,1,5)。

(2)A=2,B=1,X=1(L3:s−a−b−e−d,2,6)。

(3)A=1,B=0,X=2(L3:s−a−b−e−d,3,7)。

(4)A=1,B=1,X=1(L2:s−a−b−d,4,8)。

显然,满足条件组合覆盖的测试用例一定满足判定覆盖、条件覆盖和判定−条件覆盖。条件组合覆盖是覆盖测试中最强的一种逻辑覆盖方法。不过,它并不一定使得程序中的每一条路径都执行到,且条件组合覆盖存在冗余,增加了无意义的测试开销。

5.2.6 修正条件判定覆盖

修正条件判定覆盖(Modified Condition/Decision Coverage,MC/DC)要求每一种输入、输出都至少出现一次,每一个条件都必须产生所有可能输出结果至少一次,每个判定中的每个条件都必须能独立地影响一个判定的输出,即在其他条件不变的前提下,仅改变该条件的值而使判定结果改变,是DO-178B Level A认证标准规定的一种结构覆盖准则。当循环中存在判定时,一个测试用例下的同一判定可能被多次重复计算,但每次的条件值和判定值都可能不同,一个测试用例即可完成循环中判定的MC/DC。条件表示是不含布尔操作符号的布尔表达式;判定表示则是由条件和“0”或布尔操作符组成的一个布尔表达式。MC/DC是条件组合覆盖的子集,即基于条件和判定覆盖,对于每个条件C,都存在符合以下条件的两次计算:

(1)条件C所在判定内的所有条件,除条件C外,其他条件取值完全相同。

(2)条件C的取值相反。

(3)判定的计算结果相反。

每个判定中的每个条件独立影响判定结果至少一次,每个条件能够独立地作用于结果。对如下条件和判定,A、B、C都是一个条件,(A or B and C)是一个判定。那么,对于判定覆盖而言,就是使该判定值分别为“真”和“假”各一次,即可实现判定覆盖。

那么对于MC/DC呢?讨论如下程序:

显然,用表5-3所示测试用例,即可实现对A、B、C这3个条件的判定覆盖。

表5-3 修正条件判定覆盖测试用例

由表5-3可见,对于条件A,用例1和用例2,A取值相反,B和C取值相同,判定结果分别为1和0;对于条件B,用例1和用例3,B取值相反,A和C取值相同,判定结果分别为1和0;对于条件C,用例3和用例4,C取值相反,A和B取值相同,判定结果分别为0和1。

【定义5-1】 一个MC/DC对是一对对偶真值向量,使得判定语句产生不同结果,且不同结果仅由真值向量中一个条件值的变化所致。

由定义5-1可知,符合MC/DC对的两组真值向量,独立地影响测试结果,即某个条件的MC/DC对,该条件对测试结果的影响是唯一的。通过穷举一个判定中所有条件的真值组合以及对因果的两两对照,即可得到每个条件的MC/DC对。

表5-4 “A and B”的完备测试用例集

考虑一个仅包含一个布尔操作符的布尔表达式“A and B”,A和B的取值为{0,1},即可设计“A and B”的完备测试用例集。表5-4给出了“A and B”的完备测试用例集。

分析表5-4中第1、第2组测试用例,可得到:条件A的所有取值均出现一次;判定“A and B”的所有可能结果出现一次;条件A在条件B不变的情况下独立影响判定结果。对于第1和第3组测试用例,则有:条件B的所有取值均出现一次;条件B在条件A不变的情况下独立影响判定结果。由此得到满足MC/DC准则的测试用例集,如表5-5所示。

布尔表达式“A or B”完备测试用例集中的条件组合与“A and B”相同。那么,基于上述分析,即可得到满足MC/DC准则的测试用例集,如表5-6所示。

表5-5 “A and B”满足MC/DC准则的测试用例集

表5-6 “A or B”满足MC/DC准则的测试用例集

条件组合覆盖要求覆盖判定中所有条件取值的所有可能组合,需要大量测试用例,且随着条件数增加,组合数急剧增加。对于具有 N 个条件的布尔表达式,完备测试用例为2 N 组。当 N 值较大时,如先列出完备的测试用例集,然后选出适合的MC/DC组合,烦琐耗时。Chilenski研究发现,对于具有 N 个条件的布尔表达式,至少需要 N + 1组测试用例方可满足MC/DC准则,称为最小测试用例集,其下界为 N +1,上界为2 N

假设某判定中有3个条件,条件组合覆盖需要8个测试用例,而MC/DC需要4~6个测试用例。假设某判定中包含10个条件,条件组合覆盖需要1024个测试用例,而MC/DC仅需11~20个测试用例。MC/DC不仅具有条件组合覆盖的优势,而且能够大幅减少测试用例数。

对于一个复杂的布尔表达式,如“(A or B)and (C or D)”,自左向右,分别列出布尔表达式中的每个条件,针对条件A,任取一个可能值,比如1,让其直接作用到结果1。将B设为0,使用“or”测试用例设计方法,将(C or D)设为1,使用“and”测试用例设计方法,再次使用“or”测试用例设计方法将条件C和D设为(1,0)或(0,1),假如选用(1,0),则完成第一组测试用例设计。继续针对条件A,取其另一可能值0,不改变其他条件的值,且条件A直接作用到结果0,得到第二组测试用例。完成条件A的所有取值之后,针对条件B,选取条件B的取值直接作用到结果的测试用例作为参照对象,改变条件B的取值1,保持其他条件的值不变,条件B的取值直接作用到结果1,得到如表5-7所示的第三组测试用例集。

表5-7 “(A or B)and (C or D)”测试用例集

依照上述所讨论的方法,我们即可设计出如表5-8所示的“(A or B)and (C or D)”的最小测试用例集。

基于上述分析,可以得到如下结论:

(1)对于任何复杂的布尔表达式,都可以通过布尔计算转化为“and”和“or”最简布尔表达式。也就是说,前述“(A or B)and (C or D)”可以转化为“X and Y”形式。其中,X=A or B,Y=C or D。

(2)对于第一个条件(A or B),可以设计直接作用于结果的第一组测试用例,当对其他条件取值时,使用“and”和“or”最简布尔表达式的测试用例设计方法,不断迭代,即可覆盖所有条件。

表5-8 “(A or B)and (C or D)”的最小测试用例集

(3)对于每个条件,选取该条件直接作用到结果的测试用例为参照对象,改变该条件的取值,保持其他所有条件的值不变,逐步演进。

(4)如果某些条件的第一次取值不是唯一的,按照上述方法设计的最小测试用例集同样也不是唯一的。例如,上述第一组测试用例中条件A、C、D的取值。

这里,分别给出(A||B)&&(C&&D)、(A&&B)||(C&&D)、A&&(B||C)、(A&&B)||(C&&D&&E)以及(A&&B&&C)||(D&&(E||F)||G)共5种典型布尔表达式的MC/DC最小测试用例集,分别如表5-9~表5-13所示,读者可以参考使用。

表5-9 (A||B)&&(C&&D)的MC/DC最小测试用例集

表5-10 (A&&B)||(C&&D)的MC/DC最小测试用例集

表5-11 A&&(B||C)的MC/DC最小测试用例集

表5-12 (A&&B)|(C&&D&&E)的MC/DC最小测试用例集

续表

表5-13 (A&&B&&C)||(D&&(E||F)||G)的MC/DC最小测试用例集 B1GjTnOSr4icd/AqxzbIAd5tHt7gPxdGdErnNVyy0DX09WIuooaDxSxVeV/wquv3

点击中间区域
呼出菜单
上一章
目录
下一章
×