Download presentation
Presentation is loading. Please wait.
1
第 5 章 软 件 测 试 5.1 软件测试概述 5.2 测试方法 5.3 测试用例的设计 5.4 测试过程 5.5 调试
第 5 章 软 件 测 试 软件测试概述 5.2 测试方法 5.3 测试用例的设计 5.4 测试过程 5.5 调试 返回 主目录
2
第 5 章 软 件 测 试 5.1软件测试概述 5.1.1软件测试的目的
第 5 章 软 件 测 试 5.1软件测试概述 5.1.1软件测试的目的 统计资料表明,测试的工作量约占整个项目开发工作量的40%左右,对于关系到人的生命安全的软件(如飞机飞行自动控制系统),测试的工作量还要成倍增加。 那么,为什么要花这么多代价进行测试? 其目的何在? 它是“说明程序能正确地执行它应有的功能”,还是“表明程序没有错误”。如果是这样一个目的,就要朝着“证明程序正确”这个目标靠拢,无意识地选择一些不易暴露错误的例子。因此G.J.Myers对软件测试的目的提出了以下观点:
3
(1) 软件测试是为了发现错误而执行程序的过程。
(2) 一个好的测试用例能够发现至今尚未发现的错误。 (3) 一个成功的测试是发现了至今尚未发现的错误的测试。 因此,测试阶段的基本任务应该是根据软件开发各阶段的文档资料和程序的内部结构,精心设计一组“高产”的测试用例,利用这些用例执行程序,找出软件中潜在的各种错误和缺陷。
4
5.2 测 试 方 法 5.2.1静态测试与动态测试 1. 静态测试 静态测试是指被测试程序不在机器上运行,而是采用人工检测和计算机辅助静态分析的手段对程序进行检测,方法如下: (1) 人工测试:是指不依靠计算机而靠人工审查程序或评审软件。人工审查程序偏重于编码质量的检验,而软件审查除了审查编码还要对各阶段的软件产品进行检验。
5
(2) 计算机辅助静态分析: 指利用静态分析工具对被测试程序进行特性分析,从程序中提取一些信息,以便检查程序逻辑的各种缺陷和可疑的程序构造。如用错的局部量和全程量、不匹配参数、不适当的循环嵌套和分支嵌套、 潜在的死循环及不会执行到的代码等。还可能提供一些间接涉及程序欠缺的信息、 各种类型的语句出现的次数、变量和常量的引用表、标识符的使用方式、过程的调用层次及违背编码规则等。静态分析中还可以用符号代替数值求得程序结果, 以便对程序进行运算规律的检验。
6
2. 动态测试 动态测试指通过运行程序发现错误。一般意义上的测试大多是指动态测试。为使测试发现更多的错误,需要运用一些有效的方法。 测试任何产品,一般有两种方法:一是测试产品的功能,二是测试产品内部结构及处理过程。对软件产品进行动态测试时, 也用这两种方法,分别称为黑盒测试法和白盒测试法。
7
5.2.2黑盒测试法与白盒测试法 1. 黑盒法 该方法把被测试对象看成一个黑盒子,测试人员完全不考虑程序的内部结构和处理过程,只在软件的接口处进行测试, 依据需求说明书,检查程序是否满足功能要求。因此, 黑盒测试又称为功能测试或数据驱动测试。 通过黑盒测试主要发现以下错误: (1) 是否有不正确或遗漏了的功能。 (2) 在接口上,能否正确地接受输入数据, 能否产生正确的输出信息。 (3) 访问外部信息是否有错。 (4) 性能上是否满足要求等。
8
用黑盒法测试时,必须在所有可能的输入条件和输出条件中确定测试数据。是否要对每个数据都进行穷举测试呢?例如测试一个程序,需输入 3 个整数值。微机上,每个整数可能取值有216个,3个整数值的排列组合数为216×216×216=248≈3×1014。假设此程序执行一次为一毫秒, 用这些所有的数据去测试要用1万年!但这还不能算穷举测试, 还要输入一切不合法的数据。可见,穷举地输入测试数据进行黑盒测试是不可能的。 2. 白盒法 该方法把测试对象看作一个打开的盒子, 测试人员须了解程序的内部结构和处理过程,以检查处理过程的细节为基础, 对程序中尽可能多的逻辑路径进行测试,检验内部控制结构和数据结构是否有错, 实际的运行状态与预期的状态是否一致。
9
白盒法也不可能进行穷举测试,企图遍历所有的路径, 往往是做不到的。如测试一个循环20次的嵌套的IF语句, 循环体中有5条路径。测试这个程序的执行路径为520, 约为1014, 如果每毫秒完成一个路径的测试, 测试此程序需3170年! 对于白盒测试,即使每条路径都测试了,程序仍可能有错。 例如要求编写一个升序的程序,错编成降序程序(功能错), 就是穷举路径测试也无法发现。再如由于疏忽漏写了路径, 白盒测试也发现不了。 所以,黑盒法和白盒法都不能使测试达到彻底。为了用有限的测试发现更多的错误,需精心设计测试用例。黑盒法、 白盒法是设计测试用例的基本策略,每一种方法对应着多种设计测试用例的技术,每种技术可达到一定的软件质量标准要求。 下面分别介绍这两类方法对应的各种测试用例设计技术。
10
5.3 测试用例的设计 5.3.1 白盒技术 由于白盒测试是结构测试,所以被测对象基本上是源程序, 以程序的内部逻辑结构为基础设计测试用例。 1. 逻辑覆盖 追求程序内部的逻辑结构覆盖程度,当程序中有循环时, 覆盖每条路径是不可能的,要设计使覆盖程度较高的或覆盖最有代表性的路径的测试用例。下面根据图5.1所示的程序,分别讨论几种常用的覆盖技术。
11
1) 语句覆盖 为了提高发现错误的可能性,在测试时应该执行到程序中的每一个语句。 语句覆盖是指设计足够的测试用例,使被测程序中每个语句至少执行一次。 如图5.1是一个被测程序的程序流程图。 如果能测试路径124,就保证每个语句至少执行一次,选择测试数据为 a=2 , b=0, x=3 输入此组数据, 就能达到语句覆盖标准。
12
2) 判定覆盖 判定覆盖指设计足够的测试用例, 使得被测程序中每个判定表达式至少获得一次“真”值和“假”值,从而使程序的每一个分支至少都通过一次, 因此判定覆盖也称分支覆盖。 设计测试用例,只要通过路径124, 135或者125, 134, 就达到判定覆盖标准。 选择两组数据: a=3, b=0, x=1(通过路径125) a=2, b=1, x=2(通过路径134) 对于多分支(嵌套IF, CASE)的判定,判定覆盖要使得每一个判定表达式获得每一种可能的值来测试。
13
判定覆盖较语句覆盖严格,因为如果通过了各个分支, 则各个语句也执行了。但该测试仍不充分,上述数据只覆盖了全部路径的一半, 如果将第二个判定表达式中的“x>1”错写成“x<1”,仍查不出错误。
3) 条件覆盖 条件覆盖指设计足够的测试用例, 使得判定表达式中每个条件的各种可能的值至少出现一次。那么,上述程序中有 4 个条件: a>1, b=0, a=2, x>1
14
3) 条件覆盖 条件覆盖指设计足够的测试用例,使得判定表达式中每个条件的各种可能的值至少出现一次。那么,上述程序中有 4 个条件: a>1, b=0, a=2, x>1 要选择足够的数据, 使得图5.1中的第一个判定表达式出现结果: a>1, b=0 a≤1, b≠0 并使第二个判定表达式出现结果:
15
a=2, x>1 a≠2, x≤1 才能达到条件覆盖的标准。 为满足上述要求, 选择以下两组测试数据: a=2, b=0, x=3(满足a>1, b=0, a=2, x>1, 通过路径124) a=1, b=1, x=1(满足a≤1, b≠0, a≠2, x≤1,通过路径135) 以上两组测试用例不但覆盖了判定表达式中所有条件的可能取值,而且覆盖了所有判断的取“真”分支和取“假”分支。 在这种情况下,条件覆盖强于判定覆盖。但也有例外情况,设选择另外两组测试数据:
16
覆盖了所有条件的结果,满足条件覆盖。但只覆盖了第一个判定表达式的取“假”分支和第二个判定表达式的取“真”分支, 即只测试了路径134,此例不满足判定覆盖。 所以满足条件覆盖不一定满足判定覆盖,为了解决此问题, 需要对条件和分支兼顾。 4) 判定/条件覆盖 该覆盖标准指设计足够的测试用例,使得判定表达式中的每个条件的所有可能取值至少出现一次,并使每个判定表达式所有可能的结果也至少出现一次。对于上述程序,选择以下两组测试用例满足判定/条件覆盖:
17
a=2, b=0, x=3 a=1, b=1, x=1 这也是满足条件覆盖选取的数据。 从表面上看,判定/条件覆盖测试了所有条件的取值,但实际上条件组合中的某些条件会抑制其他条件。例如在含有“与”运算的判定表达式中, 第一个条件为“假”,则这个表达式中的后面几个条件均不起作用;在含有“或”运算的表达式中, 第一个条件为“真”,后边其他条件也不起作用,因此,后边其他条件若写错就测不出来。
18
5) 条件组合覆盖 条件组合覆盖是比较强的覆盖标准,它是指设计足够的测试用例,使得每个判定表达式中条件的各种可能的值的组合都至少出现一次。 上述程序中, 两个判定表达式共有 4 个条件, 因此有 8 种组合: ① a>1, b= ② a>1, b≠0 ③ a≤1, b= ④ a≤1, b≠0 ⑤ a=2, x> ⑥ a=2, x≤1 ⑦ a≠2, x>1 ⑧ a≠2, x≤1
19
下面 4 组测试用例就可以满足条件组合覆盖标准:
a=2, b=0, x=2 覆盖条件组合①和⑤, 通过路径124 a=2, b=1, x=1 覆盖条件组合②和⑥, 通过路径134 a=1, b=0, x=2 覆盖条件组合③和⑦, 通过路径134 a=1, b=1, x=1 覆盖条件组合④和⑧, 通过路径135 显然,满足条件组合覆盖的测试一定满足“判定覆盖”、 “条件覆盖”和“判定/条件覆盖”,因为每个判定表达式、每个条件都不止一次地取到过“真”、“假”值。但也看到,该例没有覆盖程序可能执行的全部路径,125这条路径被漏掉了,如果这条路径有错, 就不能测出。
20
6) 路径覆盖 路径覆盖是指设计足够的测试用例, 覆盖被测程序中所有可能的路径。对于上例, 选择以下测试用例, 覆盖程序中的 4 条路径: a=2, b=0, x=2 覆盖路径124, 覆盖条件组合①和⑤ a=2, b=1, x=1 覆盖路径134, 覆盖条件组合②和⑥ a=1, b=1, x=1 覆盖路径135, 覆盖条件组合④和⑧ a=3, b=0, x=1 覆盖路径125, 覆盖条件组合①和⑧ 可看出满足路径覆盖却未满足条件组合覆盖。 现将这 6 种覆盖标准作比较, 见表5 - 1。
22
语句覆盖发现错误能力最弱。判定覆盖包含了语句覆盖, 但它可能会使一些条件得不到测试。条件覆盖对每一条件进行单独检查,一般情况它的检错能力较判定覆盖强,但有时达不到判定覆盖的要求。判定/条件覆盖包含了判定覆盖和条件覆盖的要求,但由于计算机系统软件实现方式的限制,实际上不一定达到条件覆盖的标准。条件组合覆盖发现错误能力较强, 凡满足其标准的测试用例,也必然满足前 4 种覆盖标准。 前 5 种覆盖标准把注意力集中在单个判定或判定的各个条件上,可能会使程序某些路径没有执行到。路径测试根据各判定表达式取值的组合,使程序沿着不同的路径执行,查错能力强。
23
但由于它是从各判定的整体组合出发设计测试用例的, 可能使测试用例达不到条件组合覆盖的要求。在实际的逻辑覆盖测试中,一般以条件组合覆盖为主设计测试用例,然后再补充部分用例,以达到路径覆盖测试标准。
2. 循环覆盖 在逻辑覆盖的测试技术中,以上只讨论了程序内部有判定存在的逻辑结构的测试用例设计技术。而循环也是程序的主要逻辑结构,要覆盖含有循环结构的所有路径是不可能的, 但可通过限制循环次数来测试,下面给出设计原则供参考。 1) 单循环 设n为可允许执行循环的最大次数。 设计以下情况的测试用例: (1) 跳过循环。
24
(2) 只执行循环一次。 (3) 执行循环m次, 其中m<n。 (4) 执行循环n-1次, n次, n+1次。 2) 嵌套循环 嵌套循环步骤为: (1) 置外循环处于最小循环计数值, 对内层进行单循环测试。 (2) 由里向外, 进行下一层的循环测试。
25
3. 基本路径测试 图5.1的例子很简单,只有 4 条路径。但在实际问题中, 一个不太复杂的程序其路径是一个庞大的数字。为了解决这一难题,只得把覆盖的路径数压缩到一定的限度内,例如,循环体只执行一次。基本路径测试是在程序流程图的基础上,通过分析由控制构造的环路复杂性,导出基本路径集合,从而设计测试用例, 保证这些路径至少通过一次。 基本路径测试的步骤为: (1) 以详细设计或源程序为基础, 导出程序流程图的拓朴结构——程序图。
26
图5.1 一个被测试程序的流程图
27
程序图是退化了的程序流程图,它是反映控制流程的有向图,其中小圆圈称为结点,代表了流程图中每个处理符号(矩形、菱形框),有箭头的连线表示控制流向,称为程序图中的边或路径。
图5.2(a)是一个程序流程图,可以将它转换成图5.2(b)的程序图(假设菱形框表示的判断内设有复合的条件)。在转换时注意: 一条边必须终止于一个结点,在选择结构中的分支汇聚处即使无语句也应有汇聚结点; 若判断中的逻辑表达式是复合条件,应分解为一系列只有单个条件的嵌套判断,如对于图5.3(a)的复合条件的判定应画成图5.3(b)所示的程序图。
28
图 5.2程序流程图和程序图 (a) 程序流程图; (b) 程序图
29
图 5.3复合条件下的程序图 (a) 程序; (b) 程序图
30
(2) 计算程序图G的环路复杂性V(G)。 McCabe定义程序图的环路复杂性为此平面图中区域的个数。 区域个数为边和结点圈定的封闭区域数加上图形外的区域数1。 例如图5.2(b)的V(G)=4, 也可按另一种方法计算, 即V(G)=判定结点数+1。 (3) 确定只包含独立路径的基本路径集。 环路复杂性可导出程序基本路径集合中的独立路径条数, 这是确保程序中每个执行语句至少执行一次所必需的测试用例数目的上界。独立路径是指包括一组以前没有处理的语句或条件的一条路径。从程序图来看,一条独立路径是至少包含有一条在其他独立路径中未有过的边的路径,例如,在图5.2(b)所示的图中,一组独立的路径是:
31
path1: 1-11 path2: path3: path4: 从例中可知,一条新的路径必须包含有一条新边。这 4 条路径组成了图5.2(b)所示的程序图的一个基本路径集,4是构成这个基本路径集的独立路径数的上界,这也是设计测试用例的数目。只要测试用例确保这些基本路径的执行,就可以使程序中每个可执行语句至少执行一次,每个条件的取“真”和取“假”分支也能得到测试。 基本路径集不是唯一的,对于给定的程序图,可以得到不同的基本路径集。
32
5.3.2黑盒技术 黑盒测试是功能测试,因此设计测试用例时,需要研究需求说明和概要设计说明中有关程序功能或输入、输出之间的关系等信息,从而与测试后的结果进行分析比较。用黑盒技术设计测试用例的方法一般有以下 4 种,但没有一种方法能提供一组完整的测试用例,以检查程序的全部功能,在实际测试中应该把各种方法结合起来使用。 1. 等价类划分 为了保证软件质量,需要做尽量多的测试,但不可能用所有可能的输入数据来测试程序,而只能从输入数据中选择一个子集进行测试。 如何选择适当的子集,使其发现更多的错误呢?等价类划分是解决这一问题的办法。
33
表 5 - 2 中合理等价类是指各种正确的输入数据,不合理的等价类是其他错误的输入数据。 划分等价类是一个比较复杂的问题, 以下提供了几条经验供参考:
(1) 如果某个输入条件规定了取值范围或值的个数, 则可确定一个合理的等价类(输入值或数在此范围内)和两个不合理等价类(输入值或个数小于这个范围的最小值或大于这个范围的最大值)。 例如输入值是学生的成绩,范围为0~100,确定一个合理的等价类为“0≤成绩≤100”,两个不合理的等价类为“成绩<0”和“成绩>100”。
34
表5-2 等价类表 输入条件 合理等价类 不合理等价类 … … …
35
(2) 如果规定了输入数据的一组值,而且程序对不同的输入值做不同的处理,则每个允许的输入值是一个合理等价类, 此外还有一个不合理等价类(任何一个不允许的输入值)。
例如,输入条件上说明教师的职称可为助教、 讲师、 副教授及教授 4 种职称之一,则分别取这四个值作为4 个合理等价类,另外把 4 个职称之外的任何职称作为不合理等价类。(3) 如果规定了输入数据必须遵循的规则,可确定一个合理等价类(符合规则)和若干个不合理等价类(从各种不同角度违反规则)。 (4) 如果已划分的等价类中各元素在程序中的处理方式不同,则应将此等价类进一步划分为更小的等价类。
36
以上这些划分输入数据等价类的经验也同样适用于输出数据,这些数据也只是测试时可能遇到的情况的很小部分。为了能正确划分等价类,一定要正确分析被测程序的功能。
2) 确定测试用例 根据已划分的等价类, 按以下步骤设计测试用例: (1) 为每一个等价类编号。 (2) 设计一个测试用例,使其尽可能多地覆盖尚未被覆盖过的合理等价类。重复这步,直到所有合理等价类被测试用例覆盖。 (3) 设计一个测试用例,使其只覆盖一个不合理等价类。 重复这一步,直到所有不合理等价类被覆盖。
37
例如:某一报表处理系统,要求用户输入处理报表的日期。 假设日期限制在1990年1月至1999年12月,即系统只能对该段时期内的报表进行处理。如果用户输入的日期不在此范围内,则显示输入错误信息。该系统规定日期由年、月的 6 位数字字符组成,前 4 位代表年,后两位代表月。现用等价类划分法设计测试用例,来测试程序的“日期检查功能”。 ① 划分等价类并编号: 划分成 3 个有效等价类,7 个无效等价类, 如表5 - 3所示。 ② 为合理等价类设计测试用例,对于表中编号为1, 5, 8对应的 3 个合理等价类, 用一个测试用例覆盖。
39
③ 为每一个不合理等价类至少设计一个测试用例:
测试数据 期望结果 覆盖范围 99MAY 输入无效 输入无效 输入无效 输入无效 输入无效 输入无效 输入无效
40
2. 边界值分析 实践经验表明, 程序往往在处理边界情况时发生错误。 边界情况指输入等价类和输出等价类边界上的情况。因此检查边界情况的测试用例是比较高效的,可以查出更多的错误。 例如, 在做三角形设计时,要输入三角形的 3 个边长 A, B和C。 这 3 个数值应当满足A>0,B>0,C>0,A+B>C, A+C>B, B+C>A, 才能构成三角形。但如果把 6 个不等式中的任何一个“>”错写成“≥”, 那个不能构成三角形的问题恰出现在容易被疏忽的边界附近。 在选择测试用例时, 选择边界附近的值就能发现被疏忽的问题。
41
(1) 如果输入条件规定了值的范围,可以选择正好等于边界值的数据作为合理的测试用例,同时还要选择刚好越过边界值的数据作为不合理的测试用例。如输入值的范围是[1,100], 可取0,1,100,101等值作为测试数据。 (2) 如果输入条件指出了输入数据的个数, 则按最大个数、 最小个数、比最小个数少 1 及比最大个数多1等情况分别设计测试用例。 如一个输入文件可包括1~255个记录, 则分别设计有1个记录、255个记录,以及0个记录和256个记录的输入文件的测试用例。 (3) 对每个输出条件分别按照以上两个原则确定输出值的边界情况。如一个学生成绩管理系统规定,只能查询95~98级大学生的各科成绩, 可以设计测试用例,使得查询范围内的某一届或四届学生的学生成绩,还需设计查询94级、 99级学生成绩的测试用例(不合理输出等价类)。
42
由于输出值的边界不与输入值的边界相对应,所以要检查输出值的边界不一定可能,要产生超出输出值之外的结果也不一定能做到, 但必要时还需试一试。
(4) 如果程序的需求说明给出的输入或输出域是个有序集合(如顺序文件、 线性表和链表元素和最后一个元素作为测试用例。 对上述报表处理系统中的报表日期输入条件, 以下用边界值分析设计测试用例。 程序中判断输入日期(年月)是否有效, 假设使用如下语句:
43
IF(ReportDate<=MaxDate)AND(ReportDate>=MinDate)
THEN 产生指定日期报表 ELSE 显示错误信息 ENDIF 如果将程序中的“<=”误写为“<”, 则上例的等价类划分中所有测试用例都不能发现这一错误, 采用边界值分析法的测试用例如表5 - 4所示。
45
3. 错误推测 在测试程序时,人们根据经验或直觉推测程序中可能存在的各种错误,从而有针对性地编写检查这些错误的测试用例, 这就是错误推测法。 错误推测法没有确定的步骤,凭经验进行。它的基本思想是列出程序中可能发生错误的情况,根据这些情况选择测试用例。 如输入、 输出数据为零是容易发生错误的情况;又如, 输入表格为空或输入表格只有一行是容易出错的情况等。 例如对于一个排序程序, 列出以下几项需特别测试的情况: (1) 输入表为空。 (2) 输入表只含一个元素。 (3) 输入表中所有元素均相同。
46
(4) 输入表中已排好序。 又如, 测试一个采用二分法的检索程序, 考虑以下情况: (1) 表中只有一个元素。 (2) 表长是2的幂。 (3) 表长为2的幂减1或2的幂加1 因此, 要根据具体情况具体分析。 4. 因果图 等价类划分和边界值分析方法都只是孤立地考虑各个输入数据的测试功能,而没有考虑多个输入数据的组合引起的错误。 因果图能有效地检测输入条件的各种组合可能会引起的错误。
47
因果图的基本原理是通过画因果图,把用自然语言描述的功能说明转换为判定表,最后为判定表的每一列设计一个测试用例。
(1) 在任何情况下都应使用边界值分析法,用这种方法设计的用例暴露程序错误能力强。设计用例时,应该既包括输入数据的边界情况又包括输出数据的边界情况。 (2) 必要时用等价类划分方法补充一些测试用例。 (3) 再用错误推测法补充测试用例。 (4) 检查上述测试用例的逻辑覆盖程度, 如未满足所要求的覆盖标准, 再增加例子。 (5) 如果需求说明中含有输入条件的组合情况, 则一开始就可使用因果图法。
48
5.4 测试过程 5.4.1软件测试过程中的信息 软件测试时需要以下三类信息:
5.4 测试过程 5.4.1软件测试过程中的信息 软件测试时需要以下三类信息: (1) 软件配置: 指需求说明书、 设计说明书和源程序等。 (2) 测试配置: 指测试方案、 测试用例和测试驱动程序等。 (3) 测试工具: 指计算机辅助测试的有关工具。 软件经过测试以后, 要根据预期的结果对测试的结果进行分析比较,对于出现的错误要进行纠错,并修改相应文档。修改后的程序往往要经过再次测试,直到满意为止。
49
如果软件功能能正确完成,出现的错误易修改,可以断定软件的质量和可靠性可以接受或者所做的测试还不足以发现严重错误;以断定软件的质量和可靠性可以接受或者所做的测试还不足以发现严重错误; 如果测试发现不了错误,那么可以断定测试方案、 用例考虑得不够细致充分,错误仍潜伏在软件中应考虑重新制定测试方案, 设计测试用例。
50
5.4.2软件测试的步骤及与各开发阶段的关系 软件产品在交付使用之前要经过哪些测试呢?一般要经过单元测试、集成测试、确认测试和系统测试。图5.4为软件测试经历的步骤。 单元测试指对源程序中每一个程序单元进行测试, 检查各个模块是否正确实现规定的功能,从而发现模块在编码中或算法中的错误。该阶段涉及编码和详细设计的文档。各模块经过单元测试后,将各模块组装起来进行集成测试, 以检查与设计相关的软件体系结构的有关问题。确认测试主要检查已实现的软件是否满足需求说明书中确定了的各种需求。系统测试指把已确定的软件与其他系统元素(如硬件、其他支持软件、 数据和人工等)结合在一起进行测试。图5.5列出了软件工程领域中的测试与软件开发各阶段之间的关系。
51
图 软件测试步骤
52
图 5.5 软件测试与软件开发过程的关系
53
5.4.3单元测试 1. 测试的内容 单元测试主要针对模块的 5 个基本特征进行测试。 1) 模块接口 模块接口测试主要检查数据能否正确地通过模块。 检查的主要内容是参数的个数、属性及对应关系是否一致。 当模块通过文件进行输入/输出时, 要检查文件的具体描述(包括文件的定义、记录的描述及文件的处理方式等)是否正确。
54
2) 局部数据结构 局部数据结构主要检查以下几方面的错误: 说明不正确或不一致; 初始化或缺省值错误; 变量名未定义或拼写错误; 数据类型不相容;上溢、下溢或地址错等。 3) 重要的执行路径 重要模块要进行基本路径测试, 仔细地选择测试路径是单元测试的一项基本任务。 注意选择测试用例能发现不正确的计算、 错误的比较或不适当的控制流而造成的错误。 计算中常见的错误有: 算术运算符优先次序不正确; 运算方式不正确; 初始化方式不正确;精确度不够;表达式的符号表示错误等。
55
条件及控制流向中常见的错误有:不同的数据类型比较; 逻辑运算符不正确或优先次序错误; 由于精确度误差造成的相等比较出错; 循环终止条件错误或死循环;错误地修改循环变量等。
4) 错误处理 错误处理主要测试程序对错误处理的能力,检查是否存在以下问题: 不能正确处理外部输入错误或内部处理引起的错误; 对发生的错误不能正确描述或描述内容难以理解;在错误处理之前,系统已进行干预等。
56
5) 边界条件 程序最容易在边界上出错,如输入/输出数据的等价类边界, 选择条件和循环条件的边界,复杂数据结构(如表)的边界等都应进行测试。 2. 测试的方法 由于被测试的模块往往不是独立的程序,它处于整个软件结构的某一层位置上,被其他模块调用或调用其他模块,其本身不能进行单独运行,因此在单元测试时, 需要为被测模块设计驱动模块(driver)和桩(stub)模块。
57
驱动模块的作用是用来摸拟被测模块的上级调用模块,功能要比真正的上级模块简单得多,它只完成接受测试数据, 以上级模块调用被测模块的格式驱动被测模块,接收被测模块的测试结果并输出。
桩模块用来代替被测模块所调用的模块。 它的作用是返回被测模块所需的信息。 图5.6 表示为了测试软件结构(图5.6a))中的模块B,建立模块B的测试环境(图5.6(b))。 驱动模块和桩模块的编写给测试带来额外开销, 但是与被测模块有联系的那些模块(如模块M, D, E)在尚未编写好或未测试的情况下,设计驱动模块和桩模块是必要的。
58
图 5.6单元测试的测试环境 (a) 软件结构图; (b) 模块B的测试环境
59
5.4.4集成测试 1. 集成测试的目的 集成测试是指在单元测试的基础上, 将所有模块按照设计要求组装成一个完整的系统而进行的测试,故也称组装测试或联合测试。实践证明,单个模块能正常工作,组装后不见得仍能正常工作,这是因为: (1) 单元测试使用的驱动模块和桩模块,与它们所代替的模块并不完全等效, 因此单元测试有不彻底、不严格的情况。 (2) 各个模块组装起来, 穿越模块接口的数据可能会丢失。 (3) 一个模块的功能可能会对另一个模块的功能产生不利的影响。 (4) 各个模块的功能组合起来可能达不到预期要求的功能
60
(5) 单个模块可以接受的误差, 组装起来可能累积和放大到不能接受的程度。
(6) 全局数据可能会出现问题。 因此必须要进行集成测试,用于发现模块组装中可能出现的问题,最终构成一个符合要求的软件系统。 2. 集成测试的方法 集成测试的方法主要有非渐增式测试和渐增式测试。 1) 非渐增式测试 该测试是首先对每个模块分别进行单元测试, 然后再把所有的模块按设计要求组装在一起进行的测试。
61
2) 渐增式测试 该测试是逐个把未经过测试的模块组装到已经测试过的模块上去,进行集成测试。每加入一个新模块进行一次集成的测试,重复此过程直至程序组装完毕。 3) 渐增式与非渐增式测试的区别 渐增式与非渐增式测试的区别有如下几点: (1) 非渐增式方法把单元测试和集成测试分成两个不同的阶段, 前一阶段完成模块的单元测试,后一阶段完成集成测试。 而渐增式测试把单元测试与集成测试合在一起, 同时完成。
62
(2) 非渐增式需要更多的工作量,因为每个模块都需要驱动模块和桩模块,而渐增式利用已测试过的模块作为驱动模块或桩模块,因此工作量较少。
(3) 渐增式可以较早地发现接口之间的错误, 非渐增式最后组装时才发现。 (4) 渐增式有利于排错, 发生错误往往和最近加进来的模块有关,而非渐增式发现接口错误推迟到最后, 很难判断是哪一部分接口出错。 (5) 渐增式比较彻底, 已测试的模块和新的模块组装在一起再测试。
63
(6) 渐增式占用的时间较多, 但非渐增式需更多的驱动模块、 模块, 也占用一些时间。
(7) 非渐增式开始可并行测试所有模块, 能充分利用人力, 对测试大型软件很有意义。 考虑到目前计算机硬件价格下降,人工费用上升,软件错误纠正越早代价越低等特点,采用渐增式方法测试较好。也可考虑将两种方法结合起来,一些模块分别测试,然后将这些测试过的模块再用渐增式逐步结合进软件系统中去。 3. 渐增式测试的组装模块的方法
64
1) 自顶向下结合 该方法不需要编写驱动模块,只需要编写桩模块。其步骤是从顶层模块开始,沿被测程序的软件结构图的控制路径逐步向下测试, 从而把各个模块都结合进来,这里又有两种组合策略: (1) 深度优先策略:先从软件结构中选择一条主控路径, 把该路径上的模块一个个结合进来进行测试,以便完成一个特定的子功能, 接着再结合其他需要优先考虑的路径。主控路径一般选择系统的关键路径或输入、输出路径。 图5.7是一个软件结构图。 图5.8是自顶向下以深度优先策略组装模块的例子,其中Si模块代表桩模块。
65
图 一个软件结构图
66
图 5.8 采用深度优先策略自顶向下结合模块的过程
67
2) 自底向上结合 该方法仅需编写驱动模块, 不需编写桩模块。 其步骤为: (1) 把低层模块组合成实现一个个特定子功能的族(如图5.9所示)。 (2) 为每一个族编写一个驱动模块,以协调测试用例的输入和测试结果的输出(如图5.10所示,其中di模块为驱动模块)。 (3) 对模块族进行测试。 (4) 按软件结构图依次向上扩展, 用实际模块替换驱动模块, 形成一个个更大的族(如图5.11所示)。 (5) 重复(2)至(4)步, 直至软件系统全部测试完毕。
68
图 5.9 一个软件结构图
69
图 为每个族分别进行测试
70
图5.11 形成3个更大的族进一步测试
71
自底向上测试的优点是: 随着上移, 驱动模块逐步减少, 测试开销小一些;比较容易设计测试用例;早期可以并行工作; 低层模块的错误能较早发现。其缺点是:系统整体功能最后才能看到;上层模块错误发现的晚,上层模块的问题是全局性的问题,影响范围大。 由于自顶向下渐增式测试和自底向上渐增式测试的方法各有利弊,实际应用时,应根据软件的特点、任务的进度安排选择合适的方法。 一般是将这两种测试方法结合起来,低层模块使用自底向上结合的方法组装成子系统,然后由主模块开始自顶向下对各子系统进行集成测试。
72
5.4.5确认测试 确认测试又称有效性测试。它的任务是检查软件的功能与性能是否与需求说明书中确定的指标相符合。因而需求说明是确认测试的基础。 确认测试阶段有进行确认测试与软件配置审查两项工作。 1. 进行确认测试 确认测试一般是在模拟环境下运用黑盒测试方法, 由专门测试人员和用户参加的测试。确认测试需要需求说明书、用户手册等文档, 要制定测试计划,确定测试的项目,说明测试内容,描述具体的测试用例。测试用例应选用实际运用的数据。 测试结束后,应写出测试分析报告。
73
(1) 功能、 性能与需求说明一致, 该软件系统是可以接受的。
(2) 功能、性能与需求说明有差距,要提交一份问题报告。 对这样的错误进行修改,工作量非常大,必须同用户协商。 软件配置审查 软件配置审查的任务是检查软件的所有文档资料的完整性、正确性。如发现遗漏和错误, 应补充和改正。同时要编排好目录, 为以后的软件维护工作奠定基础。 软件系统只是计算机系统中的一个组成部分,软件经过确认后,最终还要与系统中的其他部分(如计算机硬件、外部设备、某些支持软件、 数据及人员)结合在一起,在实际使用环境下运行,测试其能否协调工作,这就是所谓的系统测试,系统测试有关的内容不在软件工程范围内。
74
5.5 调试 5.5.1调试的目的 软件测试的目的是尽可能多地发现程序中的错误, 而调试则是在进行了成功的测试之后才开始的工作。调试的目的是确定错误的原因和位置, 并改正错误,因此调试也称为纠错。 调试是程序员自己进行的技巧性很强的工作,要确定发生错误的内在原因和位置不是一件容易的事,它占整个调试工作量的90%左右。调试工作的困难与人的心理因素和技术因素都有关系,需要繁重的脑力劳动和丰富的经验。与测试比较,调试技术缺乏系统的理论研究, 因此介绍的调试方法多是实践中的经验积累。
75
5.5.2调试技术 1. 简单的调试方法 1) 在程序中插入打印语句 该方法的优点是显示程序的动态过程,比较容易检查源程序的有关信息。 缺点是低效率, 可能输出大量的无关的数据, 发现错误带有偶然性。 同时还要修改程序,这种修改可能会掩盖错误、改变关键的时间关系或把新的错误引入程序。 2) 运行部分程序 有时为了测试某些被怀疑为有错的程序段,整个程序反复执行多次,使很多时间浪费在执行已经是正确的程序段上。在此情况下,应设法使被测试程序只执行需要检查的程序段,以提高效率。 可采用以下方法:
76
(1) 把不需要执行的语句段前和后加上注释符, 使这段程序不再执行。调试过后,再将注释符去掉。
(2) 在不需要执行的语句段前加判定值为“假”的IF语句或者加GOTO语句,使该程序不执行。调试结束后,再撤销这些语句, 使程序复原。 3) 借助于调试工具 目前大多数程序设计语言都有专门的调试工具, 可以利用这些工具分析程序的动态行为。例如借助“追踪”功能可以追踪子程序调用、 循环与分支执行路径、特定变量的变化情况等, 利用“置断点”可以执行特定语句或改变特定变量值引起的程序中断,以便检查程序的当前状态。
77
还可借助调试工具观察或输出内存变量的值,大大提高调试程序的效率,缺点是也会产生大量的无关信息,也会走弯路。 2. 归纳法调试
归纳法是一种从特殊到一般的思维过程,从对个别事例的认识当中,概括出共同特点,得出一般性规律的思考方法。 归纳法调试从测试结果发现的线索(错误迹象、 征兆)入手分析它们之间的联系,导出错误原因的假设,然后再证明或否定这个假设。归纳法调试的具体步骤如下: (1) 收集有关数据: 列出程序做对了什么、 做错了什么的全部信息。
78
(2) 组织数据: 整理数据以便发现规律, 使用分类法构造一张线索表。
(3) 提出假设:分析线索之间的关系, 导出一个或多个错误原因的假设。如果不能推测一个假设,再选用测试用例去测试,以便得到更多的数据。如果有多个假设,首先选择可能性最大的一个。 (4) 证明假设:假设不是事实, 需要证明假设是否合理。 不经证明就根据假设改错,只能纠正错误的一种表现(即消除错误的征兆)或只纠正一部分错误。如果不能证明这个假设成立, 需要提出下一个假设。
79
例如,在一个“考试评分”程序中出现了一个错误: 在某些情况下,学生分数中间值不正确, 即51个学生评分,正确地打印出平均值是73
例如,在一个“考试评分”程序中出现了一个错误: 在某些情况下,学生分数中间值不正确, 即51个学生评分,正确地打印出平均值是73.2, 中间值却是26而不是期望的82,检查这个测试用例和其他几个测试用例的执行结果, 得到如表5 - 6的线索表。 下边通过寻找现象的矛盾来建立有关错误的假设。矛盾是取偶数个学生,计算不出错,奇数个学生计算出错,同时总结出中间值总是小于或等于学生人数(26≤51和1≤1),这时的处理可给学生换一个分数,把51个学生的测试再做一遍, 中间值仍是26,因此在“how-no”栏中填写“中间值似乎与实际分数无关”这样一个范围。
81
3. 演绎法调试 演绎法是一种从一般的推测和前提出发, 运用排除和推断过程作出结论的思考方法。演绎法调试是列出所有可能的错误原因的假设,然后利用测试数据排除不适当的假设,最后再用测试数据验证余下的假设确实是出错的原因。演绎法调试的具体步骤如下: (1) 列出所有可能的错误原因的假设:把可能的错误原因列成表, 不需要完全解释,仅是一些可能因素的假设。 (2) 排除不适当的假设: 应仔细分析已有的数据,寻找矛盾,力求排除前一步列出的所有原因。如果都排除了,则需补充一些测试用例, 以建立新的假设;如果保留下来的假设多于一个,则选择可能性最大的原因做基本的假设。
82
(3) 精化余下的假设:利用已知的线索,进一步求精余下的假设, 使之更具体化,以便可以精确地确定出错位置。
(4) 证明余下的假设: 做法同归纳法。 4. 回溯法调试 该方法从程序产生错误的地方出发,人工沿程序的逻辑路径返向搜索,直到找到错误的原因为止。例如,从打印语句出错开始,通过看到的变量值,从相反的执行路径查询该变量值从何而来。该方法是对小型程序寻找错误位置的有效方法。
Similar presentations