白盒测试又称结构测试,它的核心假设是:测试人员看得见程序的内部逻辑结构,因此能够以源代码本身为对象,设计出一批能够触达特定程序路径的测试用例。与黑盒测试只关心输入与输出之间的对应关系不同,白盒测试的出发点不是"功能对不对",而是"程序有没有哪段代码被遗漏、哪条分支从未被执行过"。在这个框架之下,逻辑覆盖就成为白盒测试中最基础、也最常考的一类准则。
逻辑覆盖,指以程序内部的逻辑结构为基础,设计若干测试用例,使得测试能够覆盖程序中的语句、判定、条件以及路径等要素的度量方法。软考教材对它的表述通常落在软件测试方法与技术这一章,与等价类划分、边界值分析、判定表、因果图等黑盒方法并列。逻辑覆盖本质上是一组由弱到强的覆盖准则,每一种准则对应一个可量化的覆盖率指标,用于回答一个根本问题:我们设计的这批测试用例,究竟把程序"测到了什么程度"。它不是一种孤立的测试技术,而是一套度量体系,衡量的是测试用例集对程序结构要素的触达能力。软件评测师科目把它列为案例分析和选择题的高频考点,软件设计师科目则在上午题中反复考查它的概念辨析与覆盖率计算。
要理解逻辑覆盖,必须先厘清四个被覆盖对象的确切含义。语句是程序中最小的可执行单位,一条语句通常对应源代码中的一行,但注意复合语句、空语句和声明语句是否计入,因语言和工具而异。判定,又称分支,指程序中的条件判断结果,一个判定通常对应一个布尔表达式,其结果要么为真、要么为假。条件,是判定内部的原子谓词,一个复合判定由若干个简单条件通过逻辑与、逻辑或连接而成。路径,则是程序从入口到出口所经历的一条完整语句序列。这四个概念层层递进,构成了逻辑覆盖从弱到强的度量基础。值得强调的是,判定和条件的分野是整个逻辑覆盖体系的逻辑起点,后续所有强弱关系的推导都建立在这个区分之上,因此初学者务必在第一时间把这两个概念吃透。
白盒测试的方法不止逻辑覆盖一种,还包括基本路径测试、数据流测试、控制流测试等。逻辑覆盖之所以在软考中被单独拎出来反复考查,是因为它直观、可量化、易出题。它的度量对象清晰,覆盖准则之间有着严格的强弱关系,非常适合用来命制客观题。同时,逻辑覆盖也是理解其他白盒方法的敲门砖,理解了覆盖度量的思想,再去看基本路径测试中圈复杂度的概念,就会顺畅得多。从这个意义上说,逻辑覆盖既是白盒测试的基础考点,也是串联整个结构测试知识体系的枢纽。
逻辑覆盖之所以会分出强弱等级,根源在于程序的控制流结构。任何一个程序都可以抽象为一幅控制流图,图中的节点是语句块,有向边表示控制转移。测试用例在控制流图上画出的轨迹,就是它的执行路径。覆盖率的高低,本质上是在问:这些轨迹覆盖了控制流图的多少种结构要素。覆盖准则的强弱,取决于它对控制流结构刻画的精细程度,刻画越细,准则越强,所需的用例也越多。
五种经典的逻辑覆盖准则之间,存在严格的包含关系。语句覆盖最弱,它只要求每条语句至少执行一次;判定覆盖要求每个判定的真假分支都被走到;条件覆盖要求每个条件的真假取值都被取到;判定条件覆盖同时要求判定和条件;条件组合覆盖要求每个判定内所有条件的取值组合都被覆盖;路径覆盖最强,要求所有可能的执行路径都被走过。这组包含关系可以概括为:语句覆盖弱于判定覆盖,判定覆盖与条件覆盖之间互不包含,判定条件覆盖同时强于二者,条件组合覆盖又强于判定条件覆盖,路径覆盖最强。理解这条包含链,是解答"哪种覆盖更强"类题目的关键。命题人在设问时,往往让考生在这条链上做判断,比如问"路径覆盖是否包含语句覆盖",答案是肯定的,因为走过所有路径必然走过所有语句。
一个极易被忽视的技术细节是:条件覆盖与判定覆盖之间并不存在谁强于谁的关系。条件覆盖要求每个条件取真、取假各一次,但如果测试用例设计不当,完全可能出现"每个条件都被取过真假,但整个判定的某个分支从未被执行"的情形。例如,对于判定表达式"如果A且B",若两个用例分别让A真B假、A假B真,那么A、B两个条件各自都取过了真和假,满足了条件覆盖,但"A且B为真"这一判定结果却从未出现。这正是判定覆盖与条件覆盖互不包含的根源,也是命题人最喜欢设陷阱的地方。反过来,判定覆盖也可能不满足条件覆盖,因为一个判定真假分支都走到,并不保证内部的每个原子条件都取到过真假。这条互不包含关系是整个逻辑覆盖考题中命题密度最高的一处,务必牢记。
在实际工程中,覆盖率由工具自动统计,但软考选择题常要求手工推算。语句覆盖率等于被执行过的语句数除以语句总数。判定覆盖率等于被走过的判定分支数除以判定分支总数。条件覆盖率等于被取到的条件取值数除以条件取值总数。路径覆盖率等于被执行的路径数除以程序路径总数。掌握这四个公式,就能在给定程序片段和测试用例时,快速计算出对应的覆盖率数值,这类计算题在软件设计师和软件评测师的真题中反复出现。计算的关键在于先画出程序的控制流图或列出判定表,逐条用例标注覆盖到的语句、分支和条件取值,再套用公式求解,切忌凭感觉估算。
控制流图是白盒测试最核心的分析工具。画控制流图时,把顺序执行的连续语句合并为一个节点,把判定点作为分支节点,用有向边连接可能的转移方向。节点之间的边代表了程序可能的执行走向。有了控制流图,覆盖率就转化成了图论问题:语句覆盖等价于覆盖所有节点,判定覆盖等价于覆盖所有边的两侧走向,路径覆盖等价于覆盖所有从入口到出口的路径。这个抽象过程看似简单,却能把原本靠直觉的判断变成精确的计数。
路径爆炸是路径覆盖在实际中难以完全实现的根本原因。一个程序若包含多个顺序判定,每增加一个判定,可能的路径数量就会成倍增长;一旦引入循环,路径数量更是趋于无穷。因此,严格意义上的全路径覆盖在绝大多数真实程序中是做不到的,测试人员转而使用基本路径覆盖,用圈复杂度限定独立路径的条数,从而把无限的问题转化为有限的问题。理解了路径爆炸,也就理解了为什么软考从不要求你穷举所有路径,而是要求你算出基本路径的条数。
语句覆盖要求设计足够多的测试用例,使程序中的每条可执行语句至少执行一次。它的价值在于能发现"某段代码从未运行"这类最明显的遗漏,但它的缺陷同样致命:语句覆盖完全不关心判定的真假走向,即使一个判定的某个分支逻辑完全写错,只要那条分支的语句被执行过,语句覆盖也会判定为通过。因此语句覆盖只能作为最低门槛,不能作为测试充分性的依据。考试中,语句覆盖常与"发现不了逻辑错误"这一局限绑定在一起考查。典型的设问是:满足语句覆盖的用例能否发现判定条件写错的缺陷,答案是否定的。
判定覆盖又称分支覆盖,要求每个判定的真分支和假分支都至少被执行一次。它比语句覆盖更进一步,因为满足了判定覆盖通常也就满足了语句覆盖。但判定覆盖依然存在盲区:当判定由多个条件复合而成时,判定覆盖只关心整个判定的结果,不关心内部单个条件的具体取值,因此它可能遗漏条件之间的逻辑错误,例如把"且"错写成"或",判定覆盖往往无法察觉。这也是判定覆盖与条件覆盖需要结合的原因,单一准则都有各自的视觉盲区。
条件覆盖要求每个判定中的每个条件都分别取真、取假各一次。它深入到判定的原子谓词层面,能发现单个条件取值被遗漏的情况。判定条件覆盖则是二者的叠加,它同时要求判定和条件都被充分覆盖,通常设计用例时,只要保证每个判定真假分支都走到、且每个条件真假取值都取到,即满足判定条件覆盖。但需要注意,判定条件覆盖并不能保证所有条件组合都被覆盖,因此它仍然弱于条件组合覆盖。在实际用例设计中,判定条件覆盖是一个性价比很高的折中,它用较少的用例换取了较强的覆盖,是工程实践中常用的目标准则。
条件组合覆盖要求每个判定内所有条件的取值组合都至少出现一次。例如一个由两个条件复合的判定,有四种真假组合,条件组合覆盖要求这四种组合全部被执行到。条件组合覆盖的强度
本篇完!