个如图5.7所示的小程序的流程图,其中包括了一个执行达20次的循环。那么它所包含的 不同执行路径数高达520(=1013)条,若要对它进行穷举测试,覆盖所有的路径。假使测试 程序对每一条路径进行测试需要1毫秒,同样假定一天工作24小时,一年工作365天,那 么要想把如图5.7所示的小程序的所有路径测试完,则需要3170年 以上的分析表明,实行穷举测试,由于工作量过大,实施起来是不现实的。任何软件开 发项目都要受到期限、费用、人力和机时等条件的限制,尽管为了充分揭露程序中所有隐藏 错误,需要针对所有可能的数据进行测试,但事实告诉我们,这样做是不可能的 软件工程的总目标是充分利用有限的人力、物力资源,高效率、高质量、低成本地完成 软件开发项目。在测试阶段既然穷举测试不可行,为了节省时间和资源,提高测试效率,就 必须要从数量极大的可用测试用例中精心地挑选少量的测试数据,使得采用这些测试数据能 够达到最佳的测试效果,能够高效率地把隐臧的错误揭露出来 (2)逻辑覆盖 逻辑覆盖是以程序内部的逻辑结构为基础的设计测试用例的技术。属白盒测试。这一方 法要求测试人员对程序的逻辑结构有清楚的了解,甚至要能掌握源程序的所有细节。由于覆 盖测试的目标不同,逻辑覆盖又可分为:语句覆盖、判定覆盖、判定一条件覆盖、条件组合 覆盖及路径覆盖 ①语句覆盖:语句覆盖就是设计若干个测试用例,运行被测程序,使得每一可执行语 句至少执行一次。这种覆盖又称为点覆盖,它使得程序中每个可执行语句都得到执行,但它 是最弱的逻辑覆盖准,效果有限,必须与其它方法交互使用。 ②判定覆盖:判定覆盖就是设计若干个测试用例,运行被测程序,使得程序中每个判 断的取真分支和取假分支至少经历一次。判定覆盖又称为分支覆盖。 判定覆盖只比语句覆盖稍强一些,但实际效果表明,只是判定覆盖,还不能保证一定能 查出在判断的条件中存在的错误。因此,还需要更强的逻辑覆盖准则去检验判断内部条件 ③条件覆盖:条件覆盖就是设计若干个测试用例,运行被测程序,使得程序中每个判 断的每个条件的可能取值至少执行一次 条件覆盖深入到判定中的每个条件,但可能不能满足判定覆盖的要求 ④判定一条件覆盖:判定一条件覆盖就是设计足够的测试用例,使得判断中每个条件 的所有可能取值至少执行一次,同时每个判断本身的所有可能判断结果至少执行一次。换言 即是要求各个判断的所有可能的条件取值组合至少执行一次 判定一条件覆盖有缺陷。从表面上来看,它测试了所有条件的取值。但是事实并非如此。 往往某些条件掩盖了另一些条件。会遗漏某些条件取值错误的情况。为彻底地检查所有条件 的取值,需要将判定语句中给出的复合条件表达式进行分解,形成由多个基本判定嵌套的流 程图。这样就可以有效地检查所有的条件是否正确了 <(A>1)∧(B=0) A X=X/A (A=2)(x1)> x-X+ X>1 X=X+1 图58(a)复合判定的例子 图S8(b)改为单个条件判定的嵌套结构的例子
11 一个如图 5.7 所示的小程序的流程图,其中包括了一个执行达 20 次的循环。那么它所包含的 不同执行路径数高达 5 20(=1013)条,若要对它进行穷举测试,覆盖所有的路径。假使测试 程序对每一条路径进行测试需要 1 毫秒,同样假定一天工作 24 小时,一年工作 365 天, 那 么要想把如图 5.7 所示的小程序的所有路径测试完,则需要 3170 年。 以上的分析表明,实行穷举测试,由于工作量过大,实施起来是不现实的。任何软件开 发项目都要受到期限、费用、人力和机时等条件的限制,尽管为了充分揭露程序中所有隐藏 错误,需要针对所有可能的数据进行测试,但事实告诉我们,这样做是不可能的。 软件工程的总目标是充分利用有限的人力、物力资源,高效率、高质量、低成本地完成 软件开发项目。在测试阶段既然穷举测试不可行,为了节省时间和资源,提高测试效率,就 必须要从数量极大的可用测试用例中精心地挑选少量的测试数据,使得采用这些测试数据能 够达到最佳的测试效果,能够高效率地把隐藏的错误揭露出来。 (2) 逻辑覆盖 逻辑覆盖是以程序内部的逻辑结构为基础的设计测试用例的技术。属白盒测试。这一方 法要求测试人员对程序的逻辑结构有清楚的了解,甚至要能掌握源程序的所有细节。由于覆 盖测试的目标不同,逻辑覆盖又可分为:语句覆盖、判定覆盖、判定-条件覆盖、条件组合 覆盖及路径覆盖。 ① 语句覆盖 :语句覆盖就是设计若干个测试用例,运行被测程序,使得每一可执行语 句至少执行一次。这种覆盖又称为点覆盖,它使得程序中每个可执行语句都得到执行,但它 是最弱的逻辑覆盖准,效果有限,必须与其它方法交互使用。 ② 判定覆盖 :判定覆盖就是设计若干个测试用例,运行被测程序,使得程序中每个判 断的取真分支和取假分支至少经历一次。判定覆盖又称为分支覆盖。 判定覆盖只比语句覆盖稍强一些,但实际效果表明,只是判定覆盖,还不能保证一定能 查出在判断的条件中存在的错误。因此,还需要更强的逻辑覆盖准则去检验判断内部条件。 ③ 条件覆盖 :条件覆盖就是设计若干个测试用例,运行被测程序,使得程序中每个判 断的每个条件的可能取值至少执行一次。 条件覆盖深入到判定中的每个条件,但可能不能满足判定覆盖的要求。 ④ 判定-条件覆盖 :判定-条件覆盖就是设计足够的测试用例,使得判断中每个条件 的所有可能取值至少执行一次,同时每个判断本身的所有可能判断结果至少执行一次。换言 之,即是要求各个判断的所有可能的条件取值组合至少执行一次。 判定-条件覆盖有缺陷。从表面上来看,它测试了所有条件的取值。但是事实并非如此。 往往某些条件掩盖了另一些条件。会遗漏某些条件取值错误的情况。为彻底地检查所有条件 的取值,需要将判定语句中给出的复合条件表达式进行分解,形成由多个基本判定嵌套的流 程图。这样就可以有效地检查所有的条件是否正确了。 图 5.8(a) 复合判定的例子 图 5.8(b) 改为单个条件判定的嵌套结构的例子
⑤多重条件覆盖:多重条件覆盖就是设计足够的测试用例,运行被测程序,使得每个 判断的所有可能的条件取值组合至少执行一次 这是一种相当强的覆盖准则,可以有效地检查各种可能的条件取值的组合是否正确。它 不但可覆盖所有条件的可能取值的组合,还可覆盖所有判断的可取分支,但可能有的路径会 遗漏掉。测试还不完全。 ⑤路径测试:路径测试就是设计足够的测试用例,覆盖程序中所有可能的路径。这是 最强的覆盖准则。但在路径数目很大时,真正做到完全覆盖是很困难的,必须把覆盖路径数 目压缩到一定限度。下面我们做一分析 (3)关于控制结构测试的一些讨论 ①分支结构的路径数 当程序中判定多于一个时,形 成的分支结构可以分为两类:嵌套 PI 型分支结构和连锁型分支结构。如 P3 图59所示。对于嵌套型分支结构 若有n个判定语句,则需要m+1个81中2中s32 测试用例:但对连锁型分支结构, s3 若有n个判定语句,则需要有2个 测试用例,去覆盖它的2条路径 当n较大时将无法测试 为减少测试用例的数目,可采 用试验设计法,抽取部分路径进行 测试。由于抽样服从均匀分布,因 (a)嵌套型分支结构 (b)连锁型分支结构 此,在假定各条路径的重要性相同 图59分支的两种类 或暂不明确各条路径的重要性的情 况下可以做到均匀抽样。如果明确了各条路径的重要性,还可以采取加权的办法,筛选掉部 分路径,再用如下的措施进行抽样。具体步骤如下: i)设耦合型分支结构中有n个判定,计算满足关系式+1≤2m的最小自然数m )设t=2m,取正交表Lt,并利用它设计测试数据。 例如,一个耦合型分支结构中有三个判定语句P1,P2,P3。它全部路径是23=8条。 先计算3+1≤2m=t的t,得t=4。取正交表L4,如图5.10(a)所示,把每一列当做一个判定 每一行当做可取的测试用例,则正交表L4最多可取三个判定,分别代之以P1,P2,P3。判 定P1,P2,P3的取假分支和取真分支分别记作Sl、S2:S3、S4;S5、S6,用各个判定的取 假分支取代正交表L4中的“0”,用取真分支取代正交表中的“1”,就建立起一个测试路径矩 阵,如图5.10(b)所示。这样,测试路径数目从23=8条减少到3+1=4条 L4123 用例P1P2P3|路径 1000 S1S355S1-S3S5 2S3S6S2-S3-S6 3S1S46S1-S4-56 4110 4 S2 S4 S5 S2-S4-S6 图5.0(a)正交表L 图5.0(b)路径抽样矩阵 ②条件测试的策略 程序中的条件分为简单条件和复合条件。简单条件是一个布尔变量或一个关系表达式
12 图 5.9 分支的两种类型 ⑤ 多重条件覆盖 :多重条件覆盖就是设计足够的测试用例,运行被测程序,使得每个 判断的所有可能的条件取值组合至少执行一次。 这是一种相当强的覆盖准则,可以有效地检查各种可能的条件取值的组合是否正确。它 不但可覆盖所有条件的可能取值的组合,还可覆盖所有判断的可取分支,但可能有的路径会 遗漏掉。测试还不完全。 ⑤ 路径测试 :路径测试就是设计足够的测试用例,覆盖程序中所有可能的路径。这是 最强的覆盖准则。但在路径数目很大时,真正做到完全覆盖是很困难的,必须把覆盖路径数 目压缩到一定限度。下面我们做一分析。 (3) 关于控制结构测试的一些讨论 ① 分支结构的路径数 当程序中判定多于一个时,形 成的分支结构可以分为两类:嵌套 型分支结构和连锁型分支结构。如 图 5.9 所示。对于嵌套型分支结构, 若有 n 个判定语句,则需要 n+1 个 测试用例;但对连锁型分支结构, 若有 n 个判定语句,则需要有 2 n 个 测试用例,去覆盖它的 2 n 条路径。 当 n 较大时将无法测试。 为减少测试用例的数目,可采 用试验设计法,抽取部分路径进行 测试。由于抽样服从均匀分布,因 此,在假定各条路径的重要性相同, 或暂不明确各条路径的重要性的情 况下可以做到均匀抽样。如果明确了各条路径的重要性,还可以采取加权的办法,筛选掉部 分路径,再用如下的措施进行抽样。具体步骤如下: ⅰ)设耦合型分支结构中有 n 个判定,计算满足关系式 n+1≤2 m 的最小自然数 m; ⅱ)设 t = 2 m,取正交表 Lt,并利用它设计测试数据。 例如,一个耦合型分支结构中有三个判定语句 P1,P2,P3。它全部路径是 2 3=8 条。 先计算 3+1≤2 m = t 的 t,得 t = 4。取正交表 L4,如图 5.10 (a) 所示,把每一列当做一个判定, 每一行当做可取的测试用例,则正交表 L4 最多可取三个判定,分别代之以 P1,P2,P3。判 定 P1,P2,P3 的取假分支和取真分支分别记作 S1、S2;S3、S4;S5、S6,用各个判定的取 假分支取代正交表 L4 中的“0”,用取真分支取代正交表中的“1”,就建立起一个测试路径矩 阵,如图 5.10 (b) 所示。这样,测试路径数目从 2 3=8 条减少到 3+1=4 条。 图 5.10 (a) 正交表 L4 图 5.10 (b) 路径抽样矩阵 ② 条件测试的策略 程序中的条件分为简单条件和复合条件。简单条件是一个布尔变量或一个关系表达式
(可加前缀NOT),复合条件由简单条件通过逻辑运算符(AND、OR、NOT)和括号连接而 成。如果条件出错,至少是条件中某一成分有错。条件中可能的出错类型有:布尔运算符错、 布尔变量错、布尔括号错、关系运算符错、算术表达式错。 如果在一个判定的复合条件表达式中每个布尔变量和关系运算符最多只出现一次,而且 没有公共变量,应用一种称之为BRO(分支与关系运算符)的测试法可以发现多个布尔运算 符或关系运算符错,以及其它错误 BRO策略引入条件约束的概念。设有n个简单条件的复合条件C,其条件约束为D=(D1 D2,…,Dn),其中D1(l≤i≤n)是条件C中第i个简单条件的输出约東。如果在C的执行过 程中,其每个简单条件的输出都满足D中对应的约束,则称条件C的条件约束D由C的执 行所覆盖。特别地,布尔变量或布尔表达式的输出约束必须是真(t)或假(f);关系表达式 的输出约束为符号 设条件为C1:B1&B2 其中B1、B2是布尔变量,C1的输出约束为(D,D2),在此,D1和D2或为t或为f。则 (t,f)是C1可能的一个约束。覆盖此约束的测试(一次运行)将令B1为t,B2为f。BRO 策略要求对C1的可能约束集合{(tt),(t),(t,f)}中的每一个,分别设计一组测试用例。 如果布尔运算符有错,这三组测试用例的运行结果必有一组导致C1失败 设条件为C2:B1&(E3=E4) 其中B1是布尔表达式,E3和E4是算术表达式,C2的输出约束为(D1,D2),在此,D 或为t或为f:D2则是<、=或>。因此,只有D2与C1中D2的不同,可以修改C1的约束 集合{(tt),(ft),(tf)},导出C2的约束集合。因为在(E3=E)中,"t相当于"=","f 相当于"<"或">",则C2的约束集合为{(L=),(f=),(t,<)(t>)}。据此设计4组测试 用例,检查C2中可能的布尔或关系运算符中的错误 设条件为C3:(E1>E2)&(E3=E4) 其中E1、E2、E3、E4都是算术表达式,C3的输出约束为(D1,D2),在此,D1和D2的约 束均为<、=、>。C3中只有D1与C2中的D1不同,可以修改C2的约束集合{(t1=)(£=) (t<),(t>)},导出C3的约束集合。因为在(E1>E2)中,"t"相当于">","T"相当于"< 或"=",则C3的约東集合为{(>,=),(<=)(=,=)(>,<)(>,>)}。根据这个约束集合设 计测试用例,就能够检测C3中的关系运算符中的错误。 ③循环测试 循环分为4种不同类型:简单循环、连锁循环、嵌套循环和非结构循环,见图5.1 嵌套循环 简单循环 连锁循环 非结构循环
13 (可加前缀 NOT),复合条件由简单条件通过逻辑运算符(AND、OR、NOT)和括号连接而 成。如果条件出错,至少是条件中某一成分有错。条件中可能的出错类型有:布尔运算符错、 布尔变量错、布尔括号错、关系运算符错、算术表达式错。 如果在一个判定的复合条件表达式中每个布尔变量和关系运算符最多只出现一次,而且 没有公共变量,应用一种称之为 BRO(分支与关系运算符)的测试法可以发现多个布尔运算 符或关系运算符错,以及其它错误。 BRO 策略引入条件约束的概念。设有 n 个简单条件的复合条件 C,其条件约束为 D =(D1, D2, …, Dn),其中 Di(1≤i≤n)是条件 C 中第 i 个简单条件的输出约束。如果在 C 的执行过 程中,其每个简单条件的输出都满足 D 中对应的约束,则称条件 C 的条件约束 D 由 C 的执 行所覆盖。特别地,布尔变量或布尔表达式的输出约束必须是真(t)或假(f);关系表达式 的输出约束为符号>、=、<。 ▪ 设条件为 C1 : B1 & B2 其中 B1、B2 是布尔变量,C1 的输出约束为(D1, D2),在此,D1 和 D2 或为 t 或为 f。则 (t, f)是 C1 可能的一个约束。覆盖此约束的测试(一次运行)将令 B1 为 t,B2 为 f。BRO 策略要求对 C1 的可能约束集合 { ( t, t ), ( f, t ), ( t, f ) } 中的每一个,分别设计一组测试用例。 如果布尔运算符有错,这三组测试用例的运行结果必有一组导致 C1 失败。 ▪ 设条件为 C2 : B1 & ( E3 = E4 ) 其中 B1 是布尔表达式,E3 和 E4 是算术表达式,C2 的输出约束为(D1, D2),在此,D1 或为 t 或为 f;D2 则是 <、= 或 >。因此,只有 D2与 C1 中 D2 的不同,可以修改 C1 的约束 集合 { ( t, t ), ( f, t ), ( t, f ) },导出 C2 的约束集合。因为在 ( E3 = E4 ) 中,"t" 相当于 "=","f" 相当于 "<" 或 ">",则 C2 的约束集合为 { ( t, = ), ( f, = ), ( t, < ), ( t, > ) }。据此设计 4 组测试 用例,检查 C2 中可能的布尔或关系运算符中的错误。 ▪ 设条件为 C3 : ( E1 > E2 ) & ( E3 = E4 ) 其中 E1、E2、E3、E4 都是算术表达式,C3 的输出约束为(D1, D2),在此,D1 和 D2 的约 束均为 <、=、>。C3 中只有 D1与 C2 中的 D1 不同,可以修改 C2 的约束集合 { ( t, = ), ( f, = ), ( t, < ), ( t, > ) },导出 C3 的约束集合。因为在 ( E1 > E2 ) 中,"t" 相当于 ">","f" 相当于 "<" 或 "=",则 C3 的约束集合为 { ( >, = ), ( <, = ), ( =, = ), ( >, < ), ( >, > ) }。根据这个约束集合设 计测试用例,就能够检测 C3 中的关系运算符中的错误。 ③ 循环测试 循环分为 4 种不同类型:简单循环、连锁循环、嵌套循环和非结构循环,见图 5.11
图5.11循环的分类 对于简单循环,测试应包括以下几种。其中的n表示循环允许的最大次数。 ■零次循环:从循环入口直接跳到循环出口 次循环:査找循环初始值方面的错误 二次循环:检查在多次循环时才能暴露的错误。 m次循环:此时的m<n,也是检査在多次循环时才能暴露的错误 最大次数循环、比最大次数多一次的循环、比最大次数少一次的循环。 对于嵌套循环,不能将简单循环的测试方法简单地扩大到嵌套循环,因为可能的测试数 目将随嵌套层次的增加呈几何倍数增长。这可能导致一个天文数字的测试数目。下面给出一 种有助于减少测试数目的测试方法 除最内层循环外,从最内层循环开始,置所有其它层的循环为最小值 对最内层循环做简单循环的全部测试。测试时保持所有外层循环的循环变量为最小值。 另外,对越界值和非法值做类似的测试 ·逐步外推,对其外面一层循环进行测试。测试时保持所有外层循环的循环变量取最小 值,所有其它嵌套内层循环的循环变量取“典型”值 反复进行,直到所有各层循环测试完毕 对全部各层循环同时取最小循环次数,或者同时取最大循环次数。对于后一种测试 由于测试量太大,需人为指定最大循环次数 对于连锁循环,要区别两种情况。如果各个循环互相独立,则连锁循环可以用与简单循 环相同的方法进行测试。例如,有两个循环处于连锁状态,则前一个循环的循环变量的值就 可以做为后一个循环的初值。但如果几个循环不是互相独立的,则需要使用测试嵌套循环的 办法来处理 对于非结构循环,应该使用结构化程序设计方法重新设计测试用例。 (4)基本路径测试 如果把覆盖的路径数压缩到一定限度内,例如,程序中的循环体只执行零次和一次,就 成为基本路径测试。它是在程序控制流图的基础上,通过分析控制构造的环路复杂性,导出 基本可执行路径集合,从而设计测试用例的方法 设计出的测试用例要保证在测试中,程序的每一个可执行语句至少要执行一次 ①程序的控制流图 控制流图是描述程序控制流的一种图示方法。基本控制构造的图形符号如图5.12所示。 符号O称为控制流图的一个结点,一组顺序处理框可以映射为一个单一的结点。控制流图中 的箭头称为边,它表示了控制流的方向,在选择或多分支结构中分支的汇聚处,即使没有执 行语句也应该有一个汇聚结点。边和结点圈定的区域叫做区域,当对区域计数时,图形外的 区域也应记为一个区域。 顺序结构 F选择结构ⅧIL重复结构UML重复结构ASE多分支结构 图5.12控制流图的各种图形符号 如果判定中的条件表达式是复合条件时,即条件表达式是由一个或多个逻辑运算符 14
14 图 5.11 循环的分类 对于简单循环,测试应包括以下几种。其中的 n 表示循环允许的最大次数。 ▪ 零次循环:从循环入口直接跳到循环出口。 ▪ 一次循环:查找循环初始值方面的错误。 ▪ 二次循环:检查在多次循环时才能暴露的错误。 ▪ m 次循环:此时的 m<n,也是检查在多次循环时才能暴露的错误。 ▪ 最大次数循环、比最大次数多一次的循环、比最大次数少一次的循环。 对于嵌套循环,不能将简单循环的测试方法简单地扩大到嵌套循环,因为可能的测试数 目将随嵌套层次的增加呈几何倍数增长。这可能导致一个天文数字的测试数目。下面给出一 种有助于减少测试数目的测试方法。 ▪ 除最内层循环外,从最内层循环开始,置所有其它层的循环为最小值; ▪ 对最内层循环做简单循环的全部测试。测试时保持所有外层循环的循环变量为最小值。 另外,对越界值和非法值做类似的测试。 ▪ 逐步外推,对其外面一层循环进行测试。测试时保持所有外层循环的循环变量取最小 值,所有其它嵌套内层循环的循环变量取“典型”值。 ▪ 反复进行,直到所有各层循环测试完毕。 ▪ 对全部各层循环同时取最小循环次数,或者同时取最大循环次数。对于后一种测试, 由于测试量太大,需人为指定最大循环次数。 对于连锁循环,要区别两种情况。如果各个循环互相独立,则连锁循环可以用与简单循 环相同的方法进行测试。例如,有两个循环处于连锁状态,则前一个循环的循环变量的值就 可以做为后一个循环的初值。但如果几个循环不是互相独立的,则需要使用测试嵌套循环的 办法来处理。 对于非结构循环,应该使用结构化程序设计方法重新设计测试用例。 (4) 基本路径测试 如果把覆盖的路径数压缩到一定限度内,例如,程序中的循环体只执行零次和一次,就 成为基本路径测试。它是在程序控制流图的基础上,通过分析控制构造的环路复杂性,导出 基本可执行路径集合,从而设计测试用例的方法。 设计出的测试用例要保证在测试中,程序的每一个可执行语句至少要执行一次。 ① 程序的控制流图 控制流图是描述程序控制流的一种图示方法。基本控制构造的图形符号如图 5.12 所示。 符号○称为控制流图的一个结点,一组顺序处理框可以映射为一个单一的结点。控制流图中 的箭头称为边,它表示了控制流的方向,在选择或多分支结构中分支的汇聚处,即使没有执 行语句也应该有一个汇聚结点。边和结点圈定的区域叫做区域,当对区域计数时,图形外的 区域也应记为一个区域。 图 5.12 控制流图的各种图形符号 如果判定中的条件表达式是复合条件时,即条件表达式是由一个或多个逻辑运算符