白盒测试中的逻辑覆盖,是软件评测师、软件设计师每年必考的核心考点。很多考生背下了"语句覆盖最弱、路径覆盖最强"这句口诀,却说不清条件覆盖与判定覆盖之间"谁包含谁"的关系,一到计算覆盖率或者判断覆盖标准层次的题目就丢分。本文从概念定义、底层原理、分类强弱、常见误区到真题关联,把逻辑覆盖的六个层次一次性讲透。
逻辑覆盖是以程序内部逻辑结构为基础设计测试用例的技术,属于白盒测试方法的核心内容。白盒测试又称结构测试、逻辑驱动测试或者基于程序的测试,其基本前提是测试人员能够看到程序的源代码,了解程序内部的语句、判定、条件与路径等逻辑结构,并据此有针对性地设计测试用例。与之相对的是黑盒测试,黑盒测试把程序视为一个只关心输入输出关系的"黑盒子",完全不考虑内部实现。逻辑覆盖正是白盒测试区别于黑盒测试最本质的特征,它回答的是一个根本问题:我们设计的这批测试用例,究竟把程序的逻辑结构"测到了什么程度"。
所谓"覆盖",指的是测试用例在执行过程中对程序逻辑结构的触碰程度。程序由一条条语句组成,语句通过顺序、分支、循环等结构组织起来,其中分支又由判定表达式和条件表达式构成。逻辑覆盖就是用覆盖率的数值来衡量测试用例对这些结构的覆盖充分性。覆盖率越高,通常意味着被测到的程序逻辑越全面,但覆盖率百分之百绝不等于程序没有缺陷,这一点在下文会专门展开。
要理解逻辑覆盖,必须先把它放回白盒测试的整体框架中。白盒测试的方法体系大致可以分为静态测试和动态测试两大类。静态测试不运行程序,靠人工检查或者工具分析代码,包括代码走查、代码审查、代码静态分析等;动态测试则需要实际运行程序,逻辑覆盖就属于动态测试范畴。在动态白盒测试中,测试用例的设计依据主要是程序的控制流结构和数据流结构,逻辑覆盖对应的是控制流维度,它关注语句、判定、条件、路径这些控制流要素是否被充分执行。因此,逻辑覆盖是白盒测试动态方法中最核心、考试频次最高的一块内容。
从测试层次上看,逻辑覆盖主要应用于单元测试阶段。单元测试针对的是最小可测试单元,通常是函数或者方法,此时测试人员能够直接看到源代码,逻辑覆盖才有发挥空间。到了集成测试、系统测试阶段,测试关注点转向模块间的接口和系统的整体行为,黑盒方法占据主导,逻辑覆盖的使用就相对有限。这一点有助于考生理解命题场景:凡是考察逻辑覆盖的题目,背景几乎都落在单元测试或者模块内部的函数级代码上。
逻辑覆盖的另一个关键概念是覆盖率。覆盖率是衡量测试充分程度的量化指标,它的通用公式可以概括为"已经覆盖到的要素数量除以要素总数"。不同覆盖标准关心的要素不同,于是就有了语句覆盖率、判定覆盖率、条件覆盖率、路径覆盖率等一系列具体指标。例如语句覆盖率等于已执行的可执行语句数除以程序中可执行语句的总数,路径覆盖率等于已执行的路径数除以程序中所有路径的总数。覆盖率从零到百分之百取值,数值越高代表覆盖越充分。
覆盖率概念的价值在于,它把"测试做得够不够"这种主观判断转化为可以计算、可以比较的客观指标。命题人经常据此出计算题:给出一个程序片段或者控制流图,要求考生计算达到某种覆盖标准所需的最少测试用例数,或者判断若干组测试用例分别达到了什么覆盖级别。能否算对这类题,取决于对六大覆盖标准定义和强弱关系的准确理解,这正是本文后续章节要解决的核心问题。
还需要说明的是,逻辑覆盖中的"覆盖"与测试充分性之间并不是简单的正比关系。覆盖率是一个必要条件而非充分条件,也就是说,覆盖率不达标通常意味着测试不充分,但覆盖率达标也不代表测试已经足够。测试用例的质量同样重要,一个精心设计、命中关键路径的用例,往往比一堆只覆盖到无关语句的用例更有价值。理解这一点,考生才能在判断"覆盖率百分之百是否等于测试完成"这类问题时给出正确结论。
逻辑覆盖能够成立,是因为程序的控制流具有清晰的图结构,可以被形式化地描述和度量。理解这层底层机制,比单纯背诵六条覆盖标准定义更能帮助考生在题目变化时快速反应。
程序的控制流可以用有向图来表示,这个图被称为控制流图。控制流图中的节点代表程序中的语句或者语句块,有向边代表语句之间的控制转移关系,即程序执行完一个节点后可能流向的下一个节点。一个典型的控制流图包含一个入口节点和一个出口节点,判定语句会形成分叉,循环结构会形成回边。有了控制流图,程序的逻辑结构就被抽象成了图论对象,覆盖问题也就转化为"用多少条从入口到出口的路径,才能把图中的节点和边走全"的问题。
在控制流图的基础上,环路复杂度给出了基本路径数量的上限。环路复杂度记为 V(G),它度量的是程序中独立路径的条数,计算公式为 V(G) = E − N + 2,其中 E 是控制流图的边数,N 是节点数。对于强连通图或者需要额外处理的图,也可以等价地用 V(G) = P + 1 来计算,其中 P 是图中判定节点的个数。环路复杂度的意义在于,它给出了完成基本路径覆盖所需要的最少测试路径数,是白盒测试中最重要的量化指标之一。命题人常直接给出节点数和边数,让考生套用公式计算。
六大逻辑覆盖标准的本质差异,在于它们关注的程序结构粒度不同。语句覆盖关注的是最小粒度,即单条语句是否被执行;判定覆盖把粒度提升到判定表达式,关心判定结果的"真"和"假"两个分支是否都走到;条件覆盖则进一步细化到判定内部的条件,关心每个条件变量的"真""假"取值是否都出现过;条件组合覆盖把粒度放大到条件取值的组合,要求每个判定中所有条件的真值组合都被穷尽;路径覆盖则从全局角度出发,关心整条执行路径从入口到出口的覆盖。
这种从语句到路径的粒度递进,决定了覆盖标准的强弱关系。粒度越细、关注的结构要素越多,覆盖标准就越强,同时满足它所需要设计的测试用例也越多。理解这一递进逻辑,考生就能在忘记具体定义时,从"覆盖粒度"这个底层线索推导出强弱顺序:语句最粗,路径最细最全,中间的判定、条件、组合依次过渡。这也是本文坚持先讲原理机制、再讲分类强弱的原因。
除控制流之外,白盒测试还关注数据流维度,即程序中变量的定义和使用关系。数据流测试通过定义-使用链来发现变量从被赋值到被引用之间的路径是否出现了异常,例如定义后从未使用、使用前从未定义等缺陷。逻辑覆盖属于控制流维度的度量,它与数据流测试相互补充,共同构成动态白盒测试的完整图景。软考命题虽然以逻辑覆盖为主,但偶尔会把数据流测试作为干扰项混入选项,考生应当了解二者的区别:逻辑覆盖看的是控制转移是否充分,数据流测试看的是数据定义与使用的对应关系是否合理。
逻辑覆盖标准按照覆盖粒度从弱到强,依次是语句覆盖、判定覆盖、条件覆盖、判定-条件覆盖、条件组合覆盖、路径覆盖。严格来说,条件覆盖与判定覆盖之间并不存在包含关系,这是整套体系中最容易出错的地方,下面逐层拆解。
语句覆盖要求设计足够的测试用例,使程序中的每条可执行语句至少被执行一次。它是所有覆盖标准中最弱的。语句覆盖的覆盖率等于已执行的可执行语句数除以可执行语句总数。它的明显缺陷在于,它只保证语句被"执行过",却不关心判定结果的走向。例如一个 if 条件被错误地写反了,只要测试用例既走了条件为真的情况又走了条件为假的情况,语句覆盖仍然可以达到百分之百,但程序逻辑的错误却完全没有被暴露。因此语句覆盖只能作为最低限度的基本要求。
判定覆盖又称分支覆盖,要求设计足够的测试用例,使程序中每个判定的"真"分支和"假"分支都至少被执行一次。判定覆盖比语句覆盖更强,因为一旦每个判定的真假两个分支都被覆盖,那么分支内部的所有语句必然都被执行过,即判定覆盖天然包含了语句覆盖。但判定覆盖仍有盲区,它只关心判定整体的真假走向,不关心判定内部多个条件的具体取值组合。例如判定表达式为 A 且 B,测试用例甲让 A 和 B 同时为真走真分支,测试用例乙让 A 和 B 同时为假走假分支,判定覆盖达到百分之百,但"A 真 B 假""A 假 B 真"这两种组合从未被测到,隐藏在组合关系中的缺陷就无法被发现。
本篇完!