浅谈测试的充分性问题

作者 |孙海英 华东师范大学软件工程学院讲师
苏亭 华东师范大学软件工程学院教授
版块 |鉴源论坛 · 观模
按语:由于无法穷举被测软件完整的输入空间,各种软件动态测试方法本质上都是围绕”如何构造测试集合以使其展现的部分行为能够高效有效地反映软件的整体行为“而展开。判断测试集合在软件上的表现是否能够充分反映该软件的总体表现,在测试领域中叫充分性问题。虽然,目前还存在尚不能圆满解决的难题和各种争议,但是这并不妨碍充分性问题作为测试领域的奠基和科学基础的地位,影响并指导着软件测试从技术到管理的方方面面。
01充分性问题的提出和变迁
1975年,goodenough和gerhart在研究软件测试是否能够保证软件的正确性(指软件与规约的一致性)时,突破性地引入了测试充分性(test adequacy)这一概念,用以确定测试数据必须具备什么性质才是一个彻底的测试,即成功的测试意味着被测程序的正确性[1]。为此,goodenough和gerhart提出了测试充分性准则的可靠性和正确性需求[2]。换句话说,能够满足测试充分性准则的可靠性和正确性的测试集合就是一个彻底的测试,能够说明被测软件的正确性。
然而,不幸的是,很快goodenough和gerhart的测试充分性理论被指出存在错误。在1976年,howden证明了goodenough和gerhart提出的充分性准则需求存在重大缺陷,通过测试的充分性保证软件的正确性是不可行的[3]。尽管如此,由于软件测试的充分性是软件在有限多个测试数据上的行为判断软件在所有输入数据上的逻辑基础,因而充分性的叫法被保留了下来。虽然不能满足提出时证明软件正确性的初衷,但是,充分性准则为分析和度量软件测试质量提供了一条客观的途径。除此以外,充分性准则还可用于确定测试过程中需要观察的内容、作为测试停止标准之一以及当测试集合未能达到期望的充分性准则时,指导测试数据的补充。
对测试集合进行期望的覆盖分析是当前判定测试充分性的主要方法。本文也从覆盖角度说明测试的充分性。实践表明,如果正确合理地运用覆盖分析,其对保证软件和软件测试本身的质量能够起到积极的作用。例如,结构覆盖分析(structural coverage analysis)是在安全关键系统领域被广泛应用的覆盖分析方法。当与“基于需求的测试”相结合(这个前提很重要,详情可参考[4]),结构覆盖分析不仅可以发现测试集合的漏测,更能发现代码中的功能缺失(本该实现而没有实现的功能)和功能多余(无中生有的功能)[4],更好地保证系统的安全性(safety)。
02有哪些主流的覆盖准则
传统上,主流的覆盖准则有控制流覆盖(control flow coverage)、数据流覆盖(data flow coverage)、变异覆盖(mutation coverage)、分域覆盖(domain partition coverage)。由于面向代码的结构,因此,常把前两者合称为结构覆盖(structural coverage)。每种覆盖根据需要满足的不同要求又包含不同的准则。图1展示了主流的覆盖准则。本文主要说明控制流覆盖和数据流覆盖。
图1 主流的覆盖准则
2.1 控制流覆盖
控制流覆盖以控制流图为测试充分程度分析基础。控制流图(control flow graph,cfg)是面向代码控制结构的抽象模型,是由块(block)和连接块的边(edge)构成的有向图。其中,块是图的节点,对应满足“原子规则”的语句序列集合,即块内的任意一条语句执行其它语句必须执行。连接块的边是图的有向边,对应代码的控制关系。后面我们用节点指代块。对于图2给出的代码,图3示例了块的划分和相应的cfg,其中,节点1为开始节点,6为终止节点。
图 2
图 3
路径是cfg中由若干相邻边连接形成的通路,采用节点序列表示。路径中含有的边的数目是路径的长度。表1列举了图3所示cfg的部分路径。因为该cfg中有回路,所以存在无数条路径。如果路径的开始节点是cfg的初始节点,终止节点是cfg的终止节点,则该路径被称为完整路径。例如,图3中给出的路径只有(1,2,6)是完整路径,其它都不是。一个测试用例的执行对应一条完整路径。
表 1
(1)语句覆盖
语句覆盖(statement coverage)要求代码的所有语句至少被执行一次。由于cfg块与语句的对应关系,因此,语句覆盖的正式定义为:测试集合t称为语句覆盖充分的,当且仅当执行t产生的完整路径集合l覆盖了控制流图中的所有节点。如果使用符号node(g)表示控制流图的节点集合,node(l)表示l包含的节点集合,则测试集合t的语句覆盖率为:
语句覆盖是级别最低的覆盖准则,但需要注意到并不是每条语句都可执行。
(2)分支覆盖
分支覆盖(branch coverage)要求代码中的所有控制转移至少被执行一次。控制转移表现为cfg的边,控制转移得到测试意味着相应的边在测试集合对应的完整路径中出现。因此,分支覆盖准则的定义如下:测试集合t称为分支覆盖充分的,当且仅当执行t产生的完整路径集合l覆盖了控制流图中的所有边。如果使用符号edge(g)表示控制流图的边集合,edge(l)表示l包含的控制流图中的边集合,则测试集合t的分支覆盖率为:
分支覆盖是比语句覆盖严格的准则,同时,也需要注意分支也不一定是可执性的。
(3)路径覆盖
路径覆盖(path coverage)要求代码中每条完整路径至少被执行一次,其定义如下:测试集合t称为路径覆盖充分的,当且仅当执行t产生的完整路径集合l覆盖了控制流图中的所有完整路径。如果使用符号path(g)表示控制流图的所有完整路径集合,则测试集合t的路径覆盖率为:
路径覆盖进一步强化了分支覆盖,是比分支覆盖更加严格的准则。实际上,由于cfg中可能存在无穷多条完整路径和不可行路径,路径覆盖的要求过于严格,以致于无法付诸实践。因此,一般情况下,会选择一个有限的完整路径子集进行测试。
(4)基路径覆盖
基路径覆盖(prime path coverage)是比路径覆盖弱的一种面向路径的覆盖准则,由jeff offut等人提出[5]。该覆盖要求构成完整路径的每一条公共子路径,即基路径,至少被执行一次。基路径是满足路径独特性规则的简单路径。所谓简单路径(simple path) 是指cfg中不含回路的路径,即除了开始节点和终止节点外,路径中每个节点出现的次数有且仅有一次。例如,表1中所列的路径,长度超过3的路径都不是简单路径。除了必须是简单路径,为了满足路径独特性规则,基路径不能是其它简单路径的子路径。例如,表1的(3,4,2)是简单路径但不是基路径,因为它是(3,4,2,3)的子路径,(3,4,2,3)是一条基路径。给定一个控制流图 g,可以通过先计算g的简单路径集合,再按照基路径定义从中筛选基路径集合的方法求解g的基路径集合。图4给出了求解过程的示例。
图 4
基路径覆盖要求每条基路径至少被执行一次,其定义如下:测试集合t称为基路径覆盖充分的,当且仅当执行t产生的完整路径集合l访问了控制流图中的所有基路径。如果使用符号pp(g)表示控制流图的所有基路径集合,pp(l)表示l访问的基路径集合,则测试集合t的基路径覆盖率为:
基路径是比分支覆盖强,但比路径覆盖弱的准则,可以有效地降低必须测试的路径数,但同样存在不可行的基路径的问题。
2.2 逻辑谓词覆盖
逻辑谓词即可以是规约中的逻辑条件,也可以指代码中的逻辑表达式。逻辑谓词决定控制转移方向,对代码功能的正确实现具有重要意义。逻辑谓词涉及条件和判定两个概念。条件指不含布尔算子的逻辑谓词,可以是布尔变量、关系表达式及其非。判定通常由条件通过布尔算子“与”、“或”、“非”连接起来的逻辑谓词。例如,(in_dis >= 0) && (in_dis = 0)和(in_dis = 0) && (in_dis = 0) && (in_dis = 0) && (in_dis = 0的假没有被执行。可见,判定覆盖不能保证构成判定的条件得到充分测试。同样,条件覆盖也不能保证判定覆盖。
(3)判定-条件覆盖
判定-条件覆盖(decision-condition coverage)用于衡量判定及构成判定的每个条件得到执行的程度。如果测试集合能够使每个判定的真假至少各被执行一次并且构成判定的每个条件的真假至少各被执行一次, 则说该测试集合满足了判定-条件覆盖。例如测试集合{in_dis = 0, in_dis = -1, in_dis = 501},满足(in_dis >= 0) && (in_dis = 0) && (in_dis = 0独立地对判定结果的影响(in_dis= 0的取值从t变为f),tc1和tc2展现了条件in_dis = 0) && (in_dis = 0为真且in_dis = 0为真且in_dis = 0为假且in_dis = 0为假且in_dis <= 500为假
2.3 数据流覆盖
程序本质上是对各种变量按照逻辑不断进行读写运算的过程。数据流覆盖从测试集合对变量读写逻辑的覆盖程度分析测试的充分性。在数据流覆盖中,写叫做定义,读叫做使用,读写逻辑通过定义-使用路径刻画。对某一变量v而言,如果程序语句将一个值存入与v相关的存储单元,则称该语句定义了变量v;如果程序语句访问了与v相关存储单元中的值,则称该语句使用了变量v。连接变量定义和使用的定义清除的简单路径,叫变量的定义-使用路径。所谓定义清除是指除了路径的开始节点外,该路径不存在变量的其它定义节点。变量的定义、使用和定义-使用路径等信息可以通过数据流图获得。图5是一个数据流图示例,通过在cfg上增加节点和边的定义和使用信息就可以得到数据流图。图中,def(n)和use(n)分别表示节点n的定义信息和使用信息。def(ni,nj)和use(ni,nj) 分别表示边(ni,nj)的定义信息和使用信息。对于变量out_dis在节点1的定义而言,(1,2,6)是一条定义-使用路径,而(1,2,3,4)则不是,因为在这条路径中,除了节点1外,还有节点4也是out_dis的定义节点。
图 5
(1)全定义覆盖
全定义覆盖(all defs coverage)要求每个变量的每个定义至少被有效使用一次。测试集合t满足全定义覆盖,当且仅当存在一个执行t产生的完整路径集合l的子路径集合包含每个变量的每个定义和某个该定义的使用构成的定义-使用路径集合。
(2)全使用覆盖
全使用覆盖(all uses coverage)要求每个变量的每个使用至少被执行一次。测试集合t满足全使用覆盖,当且仅当存在一个执行t产生的完整路径集合l的子路径集合包含每个变量的每个定义和所有该定义的使用构成的定义-使用路径集合。
(3)全定义-使用路径覆盖
全定义-使用路径覆盖(all du-paths coverage)要求所有定义-使用路径至少被执行一次。测试集合t满足全定义-使用路径覆盖,当且仅当执行t产生的完整路径集合l访问了所有数据流图中的定义-使用路径。图6给出了上述3个覆盖准则之间的差异说明。
图 6
03覆盖准则的揭错能力
揭错能力(defect detecting ability)是指测试集合发现缺陷的能力。不同的覆盖准则,揭错能力不同。研究表明,如果运用的方式合理,高级别的覆盖准则发现缺陷的能力比低级别的覆盖准则发现缺陷的能力强。覆盖准则级别的高低用“包含关系”定义:如果说a准则的覆盖级别比b准则高,即a包含b,则意味着满足a的测试集合也满足b。图7给出了逻辑谓词覆盖准则的包含情况。由图可得,多条件覆盖级别最高揭错能力最强,判定覆盖和条件覆盖之间没有关系,意味着满足判定覆盖并不一定满足条件覆盖,反之亦然。语句覆盖级别最低揭错能力最弱。
图 7
参考资料:
[1] 朱鸿, 金陵紫著. 软件质量保障与测试. 科学出版社, 1997.
[2] j. b. goodenough, s. l. gerhart, toward a theory of test data selection, ieee transaction on software engineering, se-3 (june), 1975.
[3] w. e. howden, reliability of the path analysis testing strategy, ieee transaction on software engineering, se-2, (sept.), 208–215, 1976.
[4] kelly j. hayhurst,dan s. veerhusen,john j. chilenski,leanna k. rierson,a practical tutorial on modified condition/decision coverage,technical report. nasa langley technical report server. 2001.
[5] paul ammann, jeff offutt, introduction to software testing(2nd edition), cambridge university press, 2017.


AI人工智能毕业生薪资待遇牛 起薪可高达3万-4万/月薪资上不封顶
中国移动5G消息App下架的原因是什么
才茂地质灾害隐患点自动化预警监测,让地灾防治更高效科学
Pure Storag第三季度财务业绩公布 不断推动其数字化转型
5G终端生态布局百花齐放,商用比例趋升
浅谈测试的充分性问题
日本车企大步迈入氢燃料电池车的量产阶段!
最实用最先进的智能眼镜:视线永远是清晰
各类电气控制接线图、电子元件工作原理图
定向红外对抗系统哪家强
中国无人机已打入美军“内部”,美军慌乱中担心“泄密”紧急叫停?
LED设计怎样减少输出纹波
智能制造的六大核心驱动力
Waymo会扩大和菲亚特克莱斯勒的合作
因电池供应受限,特斯拉 Semi 电动半挂卡车量产时间推迟至2024年底
云南红能售电公司正在全力推动向智能电网运营商转型
华为mate9和荣耀9都是4G+64G的配置,价格也几乎相同,该如何抉择?
国内首个消化内镜AI产品已获证,助力建设新型内镜诊疗体系
基于nRF2401的牲畜信息读写器的研究
i-VISTA自动驾驶汽车挑战赛华为MDC培训持续火爆中