西安工业大学《软件工程》第七章 测试_第1页
西安工业大学《软件工程》第七章 测试_第2页
西安工业大学《软件工程》第七章 测试_第3页
西安工业大学《软件工程》第七章 测试_第4页
西安工业大学《软件工程》第七章 测试_第5页
已阅读5页,还剩21页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第七章 测 试 编码阶段结束后,开始进入测试阶段。 无论采用何种开发模型开发出来的大型软件系统,由于客观系统的复杂性,人的主观认识的狭隘性,不 可能对要开发的系统具有完美的认识。虽然在软件开发的每一阶段都进行了技术审查和管理复审,也不可能 把设计中所有潜在的错误检查出来并进行纠正,而且在编码阶段也会引入新的错误,软件设计环节的错误, 如果不进行纠正,在使用阶段可能造成的损失会很大。所以软件在交付使用之前必须进行严格的测试,通过 测试找出软件在需求分析、设计和编码阶段隐藏的错误,并加以改正。 由于软件产品具有逻辑复杂性,所以软件测试的工作量和工作难度不亚于软件分析和设计,据统计测试 工作量占软件开发总工作量的 40%50%以上。而测试的范围存在于软件的整个生命周期,而不仅仅局限在 程序编码阶段。 7.1 测试的基本概念和原则 软件开发的前几个阶段是构建软件系统,而软件测试则尽力找出软件的失败和不足之处,表面上看设计 是建设性的,测试是破坏性的。事实上这两个过程都是为了提高软件的质量。测试是保证软件质量的重要手 段之一。 7.1.1 测试的必要性 大多数人认为在进行软件开发时,认为对软件系统编码结束后,整个软件系统的开发已经基本完成,对 测试的理解是把程序看一遍,如果发现了错误,记录错误然后通过调试改正错误,测试过程就结束了。 在测试理论形成的早期,一些软件开发公司由于忽视软件的测试,造成了巨大的经济损失。所以测试对 提高软件的质量具有很大的重要性。虽然在软件生命周期的各个阶段都有严格的技术审查和管理复审,但是 经验表明,审查不能发现所有的差错;在编码过程中,程序员的编程水平对软件的影响很大,但是不管经验 多丰富的程序员在编程中都会引入一些错误。如果这些错误在软件交付前没有被测试出来,投入使用后往往 带来严重后果。 7.1.2 测试的概念 软件测试 软件测试是对软件计划、软件设计和软件编码进行查错和纠错的活动,这个过程包括了代码执行活动和 人工活动。测试的目的是找出软件开发整个周期中各个阶段的错误,分析错误的性质和位置而加以纠正。纠 正的过程包括对文档和代码的修改,找错的活动称为测试,而纠错的过程称为调试。软件测试过程覆盖软件 开发的整个阶段。 程序测试 程序测试是对编码阶段出现的语法错、语义错、运行错进行查找的编码执行过程。通过查找编码错和纠 正编码错来保证算法的正确实现。软件测试过程覆盖软件开发的整个阶段,程序测试则仅限于编码阶段。 动态测试 动态测试包括白盒测试和黑盒测试。黑盒测试是根据软件的功能而设计测试用例而进行测试的过程,白 盒测试是根据测试的内部结构而设计测试数据,发现程序的错误。 静态测试 静态测试包括代码审查和静态分析。 代码审查是由有经验的程序设计人员根据软件详细设计说明书,通过阅读程序发现软件的错误和缺陷。 主要检查代码和设计的一致性、可读性、代码逻辑表达的正确性和完整性、代码结构的合理性等。这种方法 不需要专门的测试工具和设备,一旦发现错误就能定位错误,但是此方法具有一定的局限性。 静态分析,主要对程序进行控制流分析、数据流分析、接口分析和表达式分析等。静 态分析一般由计算机辅助完成,由于使用的程序设计语言不同,相应的静态分析工具也就 不同。目前,具备静态分析功能的软件测试工具有很多,如针对汇编语言和 C 语言开发了一些静态测试分析 工具。 7.1.3 软件测试的目的 在进行测试之前,正确理解测试的目的是至关重要的。测试的目的决定了测试方案的设计。某些人认为 “测试是为了表明程序是正确的”或“成功的测试是没有发现错误的测试”等等,基于这种思想对软件进行 测试,会设计一些不易暴露错误的测试方案;相反,如果认为测试是为了发现软件中的错误,就会设计出最 能暴露错误的测试方案。 G.Myers 关于软件测试目的的观点是: (1) 测试是为了发现程序中的错误而执行程序的过程; (2) 好的测试方案是极有可能发现迄今尚未发现的尽可能多的错误的测试; (3) 成功的测试是发现了迄今尚未发现的错误的测试。 由上述可知,测试的目的是应该尽量找出软件中隐藏的错误并加以纠正,而不是通过测试证明软件没有 错。所以,通过测试不是证明软件是正确的。通过测试只能从软件中找到错误,而不可能证明程序中没有错 误,即使选择测试方案最完美,软件中仍然可能隐藏着错误。从表面上看,设计是构造,而测试是破坏,但 两者的目的都是开发出高质量的软件产品。 明确了测试目的,从心理学的角度,由程序的编写者测试自己编写的软件是不恰当的,但综合测试阶段 通常由其他人员组成测试小组完成测试工作。 7.1.4 测试复杂性 测试任何产品一般有两种方法:黑盒测试和白盒测试。如果已经知道了产品应该具有的功能,采用黑盒 测试检验产品的每个功能是否能够正常使用;如果知道产品的内部工作过程,采用白盒测试来检验产品内部 动作是否按照规格说明书的规定正常运行。 对于软件测试而言,黑盒测试只在程序的接口进行测试,检查程序是否完成规格说明书上规定的功能, 是否能够适当地接受输入数据产生正确的输出信息,并且保持外部数据的完整性,即黑盒测试把测试对象看 成一个黑盒子,完全不考虑其程序的内部结构与处理过程,该测试又称为功能测试。而白盒测试按照程序的 内部逻辑结构,检验程序中的每条通路是否能够按照规格说明书上的规定正确工作,即把程序看成装在一个 透明的盒子里,完全了解程序的结构和处理过程,该测试又称作结构测试。 在理论上看,不论采用哪种测试,只要对每一种可能的情况都进行测试,就可以得到完全正确的程序。 我们称包含所有可能的测试称为穷尽测试。而在实际中,穷尽测试是不可能做到的。 使用黑盒测试时,要做到穷尽测试,必须对所有的输入数据 的各种可能值的所有排列组合都进行测试。 例1. 一程序模块如图 7.1。 由三个整数型的输入数据, 如果计算机字长为 16 位, 则每个 整数可能取的值有 216个,三个输入数据的各种可能值的排列组 p模块 x y z 图7.1 黑盒测试复杂性例子 合共有21621621624831014。也就是说,大约需要把这个程序执行 31014次才能做到“穷尽”测试。 假定每执行一次程序需要一毫秒,执行这么多次大约需要 1 万年!不仅测试时间长得叫人不可思议,测试得 出的输出数据更是多得叫人完全无法分析。然而严格地说这还不能算穷尽测试,为了保证测试能发现程序中 的所有错误,不仅应该使用有效的输入数据(对这个例子来说是合法的整数),还必须使用一切可能的无效输 入数据(例如,不合法的整数、实数、字符串等等)。实践表明,用无效的输入数据测试比用有效的输入数据 进行测试,往往能发现更多的错误。 使用白盒测试时要做到穷尽测试,要使程序中的每条可能 通路至少都执行一次,严格地说每条通路都应该在每种可能的 输入数据下执行一次。 例 2. 一段程序对嵌套的 IF 语句循环执行 20 次(流程图如 图 7.2)。在这段程序中共有 520(1014)条可能的执行通路,显 然,即使每条通路只执行一次也是不可能的。 从上面的例子可以得到这样的结论:无论采用黑盒法还是 白盒法,要做到穷尽测试,在时间上和代价上都不可能,只能 选择部分测试用例尽可能多地行测试,找到尽可能多的错误, 也即是进行有穷测试。 7.1.5 测试的基本原则 从上面介绍的测试目的和测试的复杂性,要求测试人员在进行程序测试时,为了达到测试的要求,应该 遵循一些原则,即测试原则。 (1) 测试前要认定被测试的软件有错,不要认为被测试的程序是正确的; (2) 要确定测试软件的预先测试结果; (3) 尽量避免测试自己编写的程序; (4) 测试时要考虑合理的输入和不合理的输入数据; (5) 测试时应以软件需求规格说明书中的需求为标准; (6) 要确定找到的新错与已找到的旧错成正比; 人们在解决问题时如果在对问题的理解或者问题求解方法的描述中有一处不正确,其相 关部分的处理办法也可能是错误的。 IBM370 系统中, 47的错误集中在 4的模块中, 说明软件错误具有 “局 部化聚集”特征。GMyers 曾经指出: “一个或者多个模块中存在错误的概率与其中已经发现的错误个数成 正比” 。因此应该对已经发现错误集中的模块进行重点测试,以期找出相关的可能错误,提高测试效率。 (7) 所使用的测试用例应该纪录下来,以后测试时再用,以供后来的测试和维护使用。 这样做的好处有二,首先是测试具有可重复性,在确认测试时对用户特别重要,而且所使用的测试用例 和执行结果所得到的数据是建立系统的可靠性模型,评价软件质量的依据。 其次,测试用例是系统维护测试与确认的依据,在将来的运行维护阶段,对系统进行局部修改后,必须对修 改程序的有效性进行确认。 7.2 测试步骤 在测试一个大规模的软件系统之前,测试人员应该清楚测试的整个过程以及测试的步骤。 循环 20次 图7.2 白盒测试复杂性例子 7.2.1 测试过程 整个测试过程如图 7.3。 图7.3 软件测试过程 输入信息包括软件配置、测试配置。其中软件配置包括软件需求规格说明书、软件系统设计说明书、源 程序清单,可运行代码系统,用户文档(用户技术手册、用户使用手册等) 。 测试配置包括软件测试计划和测试方案(测试工具+测试数据+测试功能+预期结果) ,测试工具是为提高 测试效率而设计的支撑软件测试的软件。测试结果是执行测试方案得到的实际输出结果。 可靠性模型:通过对测试软件的出错率的分析,建立模型,取得可靠的数据,指导软件的维护。 输出信息包括修正软件的文档或预测的可靠性模型或纠错后可交付使用的正确软件。 测试的过程是相对有限的过程,而不是无限的过程。 测试结束后,对测试结果得到的数据进行分析,比较预期结果与测试执行结果。如果出现的错误较多, 系统不能达到主要功能、性能指标,则软件质量与可靠性值得怀疑,就需要仔细分析并进一步测试。如果测 试发现的错误较少,则应该做如下分析。 软件是可以接受的; 测试所使用的测试用例不能发现系统中可能存在的错误。 通过测试, 一个错误都没发现, 则应该认为 这样的测试是失败的测试。 7.2.2 测试的步骤 对于一个大型的软件系统来说,在进行系统测试时,必须按照一定的步骤进行,才能在测试时发现更多 的错误。测试时,首先进行单元测试,再进行组装测试,最后进行确认测试。 1模块测试 又称单元测试。在结构化软件系统中,每个模块完成一个相对独立的子功能,将每个模块作为一个独立 的实体进行测试,确认模块作为一个单元能够正常运行。在该测试阶段所发现的错误通常是编码和详细设计 的错误。 2系统集成测试 又叫接口测试、组装测试。将经过测试的单元模块按照一定的顺序组装成系统,同时进行测试。集成测 试的重点是模块间的相互通信与协调。在进行集成测试时,应该考虑的问题有以下几个方面: (1) 各模块组织在一起,相互传递的数据是否正确,是否有丢失或者不匹配; (2) 一个模块功能实现的副作用对其他模块产生何种影响; (3) 系统的全局数据的组织是否合适,是否会发生冲突; (4) 单个模块的计算误差的累积是否得到有效控制; (5) 各模块组织起来是否能够实现需求分析规定的要求; (6) 从最后实现的角度看,系统需求分析本身是否完整,各功能实现是否相容和一致。 当系统规模比较庞大时,进行集成测试一般分为子系统集成测试和全部系统集成测试。在系统集成测试 过程中,不仅要发现设计与编码的错误,还应该验证系统是否能够实现需求分析规定的功能与性能。因此, 该阶段所发现的错误既可能是设计错误,又可能是需求分析中的错误。 3确认测试 又称合格测试或验收测试。该阶段将软件系统作为一个单一的实体,在用户的积极参与下进行测试。由 于该阶段是用户接受软件系统的最后步骤,同时也是软件系统交付的必需环节。测试所使用的数据集常常是 实际数据。确认测试的目的是验证系统是否能够达到项目计划规定的要求。对于关系重大的软件系统,在验 收以后并不立即投入正式运行,一般要有一个试运行阶段,这样做的优点是: (1) 在准生产环境中运行新系统既可以进一步确认系统,又可以减少技术风险; (2) 使用户熟悉系统的运行特征; (3) 检验用户文挡(用户指南、用户手册、维护手册等)是否完整和正确; 试运行一般分为测试和测试两个过程。 测试由一个用户在开发环境下模拟实际操作环境运行程序系统。测试的目的是评价软件产品的功能、 可用性、可靠性、性能和支持,尤其是系统的界面和特色。测试人员是除开发人员以外首先见到系统的用户, 他们发现的问题和修正意见通常十分有价值。而开发者在用户身边随时记录系统出错情况和使用中存在的问 题。测试可以在集成测试完成后进行,也可以在模块或子系统测试完后进行。 测试由一个或者多个用户在实际操作环境中运行系统。这些用户是与开发公司签订了支持产品预发行 合同的外部用户或者是志愿者,要求他们使用产品,并积极返回运行过程中发现的错误信息给开发者。测 试的重点在于系统的可支持性,包括系统文档的完整性、用户培训和支持、使用系统的能力和满意程度。与 测试的不同点在于:开发者不在用户的测试现场,而测试过程中用户纪录的问题可能是系统存在的错误, 也可能是用户的主观认识。 整个软件测试步骤如图 7.4。 图7.4 测试步骤 7.3 设计测试方案 这一节主要介绍在采用白盒测试和黑盒测试中所使用的技术。 7.3.1 白盒法测试的基本技术 在工程实践中,白盒法的基本测试思想:分析被测试程序的逻辑结构,选择逻辑执行路径子集设计测试 方案。用尽可能少的测试用例子集确定被测试程序的实际执行状态与预期的状态是否一致,期望发现尽可能 多的错误。 但是如何选择路径子集呢?在工程实践中提出了以下几种选择逻辑路径子集的覆盖标准。 1语句覆盖 执行足够的测试用例,使得被测试程序中每个可执行语句至少被执行一次。 例3:一程序的流程图如图 7.5: 开始 A1 and B=0 X:=X/4 A=2 or x1 X:=X+1 结束 T F F T a b d c e 图7.5 白盒测试例子流程图 在该例中,要满足语句覆盖标准,即使程序的每个语句执行一次,只需两个判定表达式 和(2都取真值,程序执行的路径可以是:, 为覆盖该路径可选择的测试用例为:2,只使用这一组数据就足够了。 (1)(0Aand B= ) )(1Aor X=()p acbed ,0,5ABX= 语句覆盖标准只是测试了程序中各语句的语法及有限的数据逻辑关系,而程序中的控制逻辑被忽略。在 例子中两个判定条件都只测试了条件为真的情况,如果条件为假时处理有错误,显然不能发现。语句覆盖只 关心判定表达式的值,而没有分别测试判定表达式中每个条件取不同值时的情况。如果程序中把第一个判定 表达式中的逻辑运算符“and”错写成“or” ,或把第二个判定表达式中的条件“1”误写成“;1:1FA2:0TB =2:0FB ;3:2TA=;3:2FA;4:1TX 4:1FX 。 为了达到条件覆盖的标准,选择的测试用例如表 7-2: 表7-2 条件覆盖的测试数据 测试数据 覆盖条件 判定分支 覆盖路径 2,0,0ABX= T1,T2,T3,F4 TT ()p acbed 1,1,4ABX= F1,F2;F3,T4 FT ()p abed 通常希望条件覆盖强于判定覆盖,但是由于条件覆盖是对构成判定的条件进行分解以后孤立地满足各条 件的可能取值,因此设计的测试用例可能满足条件覆盖但未必满足判定覆盖,而且满足判定覆盖也不一定满 足条件覆盖,所以引入了下面的能同时满足这两种覆盖标准的逻辑覆盖标准。 4判定/条件覆盖 它的基本思想是:执行足够的测试用例,使得被测试程序中每个判定分支至少获得一次通过,同时各判 定中每个条件至少获得一次可能取值的机会。 对于上面的例子,满足判定/条件覆盖的逻辑标准,选择的测试数据见表 7-3。 表7-3 判定/条件覆盖的测试数据 判定/条件覆盖标准从表面上看似乎考虑了所有条件的取值, 同时照顾了判定的各个分支路径。 但实际上 在判定构成中,某个条件的取值可能屏蔽了另一个条件。比如判定表达式A1 and B=0,如果A1 为真,则 须测试B0 的取值是否为真;如果 A1 为假,则无须检测B0 的取值了,所以 B0 条件没有被测试。所 以该覆盖标准不一定能测试出逻辑表达式中的错误,因此又引出了下一个覆盖标准。 5条件组合覆盖 条件组合覆盖是更强的逻辑覆盖,它的基本思想是:执行足够的测试用例,使得被测试程序中每个判定 条件的各种组合至少获得一次为真和假的机会。 满足条件组合覆盖标准,所选择的测试数据见表 7-4。 表7-4 条件组合覆盖的测试数据 测试数据 覆盖条件 判定分支 覆盖路径 2,0,6ABX= T1,T2,T3,T4 TT p acbed 1, 1, 2=XBA T1,F2,T3,F4 FT p abed 0,0,2ABX= F1,T2,F3,T4 FT p abed 0, 1, 0=XBA F1,F2,F3,F4 FF p abd 2, 0, 2=XBA T1,T2,T3,T4 TT p acbed 2, 2, 3=XBA T1,F2,F3,T4 FT p abed 1, 3, 3=XBA T1,F2,F3,F4 FF p abd 满足条件组合覆盖的测试数据,一定也满足判定、条件、判定/条件覆盖标准,所以条件组合覆盖是一种 比较强的覆盖标准。但是,应该注意到满足条件组合覆盖的数据并不一定能执行程序中的每条路径。 以上介绍的覆盖划分标准,是根据数据对源程序语句检测的详尽程度。如果将程序逻辑流程中每个条件 判定框和处理框看作一个节点,则流程图可抽象为一个有向图,称为流程图,正常情况下程序图是单向连通 的。图7.5 的程序图如图7.6,称为 G 图。测试数据也可以通过检测程序的路径的多少,来反映对程序测试的 详尽程度。从对程序路径的覆盖分析,有以下几个覆盖标准: 测试数据 覆盖条件 判定分支 覆盖路径 2,0,6ABX= T1,T2,T3,T4 TT p acbed 1, 1, 2=XBA T1,F2,T3,F4 FT p abed 3, 0, 1=XBA F1,T2,F3,T4 FT p abed 1,1,1ABX= F1,F2,F3,F4 FF p abd 6点覆盖 如果选择的测试用例对应的逻辑执行路径构成有向图 G是程序图 G 的 单向连通子图,G中包含 G 的全部点,则称 G是 G 的点覆盖。要满足点覆 盖标准要求选择足够多的数据,使得执行路径至少经过程序图中的每个结点 一次,它其实就是语句覆盖。 S A B C D a b c d e f g 图7.6 图7.5 流程图的程序图 7边覆盖 如果选择的测试用例对应的逻辑执行路径构成有向图 G是程序图 G 的 单向连通子图,G中包含G 的全部边,则称 G是 G 的边覆盖。 为了满足该覆盖标准,应该选取足够多的测试数据,使得程序执行路径 至少经过程序图中每条边一次。例如,可能选择的测试数据如表 7-5。 表7-5 边覆盖测试数据 测试数据 覆盖路径 3,0,3ABX= p adec 2,1,1ABX= p abfg 8路径覆盖 该标准的含义是:执行足够的测试用例,使得被测试程序中的每条可能路径至少执行一次(如果对应程 序途中有回路,则要求每个回路至少通过一次) 。例如为满足该标准可能选择的测试数据如表 7-6。 表7-6 路径覆盖测试数据 测试数据 覆盖路径 1,1,1ABX= p abc 1,1,2ABX= p abfg 3,0,1ABX= p adec 2,0,4ABX= p adefg 路径覆盖是一个最强的覆盖标准,它保证了每个可能的逻辑路径至少执行一次,使测试数据更具有代表 性,暴露问题的能力比较强。但是,路径覆盖中,只是考虑了每个判定表达式的取值,并没有检验表达式中 条件的各种可能组合,所以在实际情况中,常常是将路径覆盖与条件组合覆盖相结合使用,可以设计出检错 能力更强的测试数据。 7.3.2 黑盒法测试的基本技术 进行无穷的黑盒测试在实际中是不可能的。黑盒测试的技术就是如何选择尽可能少的测试用例覆盖被测 试程序模块的尽可能多的合理输入/输出数据子集和无效数据子集,以期望暴露出隐藏在程序中较多的错误。 等价分类法 该方法的基本思想是将程序模块的所有可能输入数据(有效与无效的)划分为若干等价类,在指定等价 类中任取一组数据作为一个测试用例,作为该等价类的代表,并且假定:如果该组数据可以检查出程序的错 误,则该等价类中其他组数据也可以产生同样的错误,相反,如果该组数据没有查出错误,则使用该等价类 中其他数据组执行程序也是正确的。 等价分类法的最关键的问题是如何划分等价类。一般有两种类型的等价类: 合理等价类:输入的数据满足程序模块要求的输入数据规范。 不合理等价类:输入的数据不满足程序模块对输入数据的要求。 而如何划分等价类,经验很重要。在实际中,对输入数据和输出数据划分等价类时,所使用的启发式规 则有以下几条: (1) 如果确定了输入值的范围,则可以划分一个有效等价类和两个无效等价类; (2) 如果确定了输入数据的个数,则可以类似地划分一个有效等价类和两个无效等价类; (3) 如果规定了输入数据的一组值,而且程序对不同输入值做不同的处理,则每个允许的输入值是一个 有效的等价类,此外还有一个无效的等价类(任一个不允许的输入值) 。 (4) 如果规定了输入数据必须遵循的一组规则,则可以划分一个有效等价类和分别不满足各条规则的若 干无效等价类; (5) 如果规定了输入数据的类型,则可以设置一个有效等价类和输入其他类型数据的若干无效等价类; (6) 如果程序模块的处理对象是线性表,则应该考虑存在一个记录、多个记录和空表的情况; (7) 如果输出结果的条件能够确定,则应该为各种可能的输出情况设置合理的、不合理的等价类。 结合上面介绍的启发式规则,可以划分有效和无效等价类,根据等价类来设计测试方案。一般按照如下 的步骤进行: 第一步:设计一个新的测试方案使其尽可能多地覆盖尚未被覆盖的有效等价类,重复这一步骤,直到所 有有效等价类被覆盖为止。 第二步:设计一个新的测试方案,使其覆盖一个而且只覆盖一个尚未被覆盖的无效等价类(通常程序发 现一类错误后就不再检查是否还有其他错误, 因此, 应该使每个测试方案只覆盖一个无效的等价类) , 重复这 一步骤直到所有无效等价类都被覆盖为止。 边界值分析 程序在处理边界情况时最容易发生错误,许多程序错误出现在下标、数据结构和循环等等的边界附近。 因此,设计程序运行在边界情况附近的测试方案,使程序错误容易暴露出来。 使用边界值分析方法设计测试方案首先应该确定边界情况, 这需要经验和创造性, 通常输入等价类和输出 等价类的边界,就是应该着重测试的程序边界。选取的测试数据应该等于、稍小于和稍大于边界值,而不是 选取每个等价类内的典型值或任意值作为测试数据。 错误推测 使用边界值分析和等价划分技术,可以设计出具有代表性的、容易暴露程序错误的测试方案,但是不同 类型不同特点的程序通常又有一些特殊的容易出错的情况。此外,有时分别使用每组测试数据时程序都能正 常工作,这些输入数据的组合却可能检测出程序的错误。 错误推测法在很大程度上靠直觉和经验进行。它的基本思想是:列举出程序中可能有的错误和容易发生 错误的特殊情况,并且根据它们选择测试方案。 对于程序中容易出错的情况也有一些经验总结出来,例如,输入数据为零或输出数据为零往往容易发生 错误;如果输入或输出的数目允许变化(例如,被检索的或生成的表的项数),则输入或输出的数目为 0 和 1 的情况(例如,表为空或只有一项)是容易出错的情况。还应该仔细分析程序规格说明书,注意找出其中遗漏 或省略的部分,以便设计相应的测试方案,检测程序员对这些部分的处理是否正确。 选择输入组合的一个有效途径是把计算机测试和人工检查代码结合起来。例如,通过代 码检查发现程序中两个模块使用并修改某些共享的变量,如果一个模块对这些变量的修改不 正确,则会引起另一个模块出错,因此这是程序发生错误的一个可能的原因。应该设计测试 方案,在程序的一次运行中同时检测这两个模块,特别要着重检测一个模块修改了共享变量 后,另一个模块能否像预期的那样正常使用这些变量。反之,如果两个模块相互独立,则没有必要测试它们 的输入组合情况。通过代码检查也可以发现模块相互依赖的关系。 7.4 单元测试 单元测试的对象是软件结构的基本单位模块,测试的方法一般采用白盒法,以路径覆盖为最佳测试 准则。一般在编码阶段就开始进行了。 7.4.1 单元测试的内容 进行单元测试时主要从以下四个基本特性出发: 模块接口测试 通过测试模块接口的数据流是否可以通畅地流入出模块,如果数据不能正常地进出,则其他测试都不 能进行。模块接口测试的重点在以下几个方面。 (1) 模块接受的输入参数(实参)个数是否与模块的参数(形参)在数目、属性、单位上一致; (2) 该模块传送给被调用模块的输入参数(实参)与被调用模块的实参在数目、属性、单位上是否一致; (3) 模块调用内部函数参数个数、属性、次序是否一致; (4) 模块内部是否发生了对输入参数进行了修改; (5) 全局数据(变量)的定义和用法在各模块中是否一致; 如果该模块执行了外部的 I/O 操作,还应该考虑如下的问题: (6) 打开文件的语句是否正确,文件的属性是否正确,格式说明是否与输入/输出语句一致; (7) 缓冲区的大小是否与记录长度相匹配; (8) 文件指针初始化是否正确,文件的结束判断是否正确,I/O 错误是否进行检查和处理。 局部数据结构 测试模块内部数据是否完整,内容、形式、相关关系是否正确。这常常是软件错误的主要来源。局部数 据测试主要针对以下几点: (1) 数据结构说明是否完整、正确和相容; (2) 数据类型是否相容,是否出现溢出或地址异常; (3) 局部变量的名字是否有错,引用前是否初始化及初始值是否正确; (4) 局部变量与全局变量是否同名; 逻辑覆盖(执行路径)问题 通常,不可能对模块内部的所有逻辑执行路径进行测试,所以选择最有可能发现错误的执行路径进行测 试是至关重要的。进行路径测试时,主要考虑的问题有: (1) 运算的优先次序是否正确; (2) 混合运算的数据类型是否相容,表达式是否正确,数据精度是否满足要求; (3) 循环条件是否正确,终止条件是否正确,循环中的加 1 或减1 是否正确; (4) 边界值的问题,比如数组的第一个数据或最后一个数据的处理。 出错处理问题 一个好的模块应该能够分析出错的原因、报告出错和提供很好的处理错误,保证程序能正常的运行。因 此应该详细测试错误处理的通路。主要包括以下几点: (1) 是否详细、通俗易懂地描述了可能遇到的错误; (2) 报告的错误信息是否与实际错误相符; (3) 出错的条件是否被系统干预; (4) 错误定位、错误处理是否正确。 7.4.2 单元测试步骤 大多数的单元测试是由程序员在编码阶段进行的,但是对于决定系统主要功能和关键性能的模块应该进 行独立测试。在进行正式测试之前,需要代码审查组阅读、检查代码,进行静态代码复审,这样可以查出部 分错误。代码审查组由组长、程序设计者、程序编写者和程序测试者组成,而组长由软件设计能力很强的高 级程序员担任。 单元测试按照如下的步骤进行。 1配置测试环境,设计所需要的辅助模块 在测试指定模块时,与该模块相关的其他模块未必已经完成。因此为了测试该模块,必须单独编制若干 与之相关的虚模块代码, 即辅助模块, 构成被测试模块运行的最小环境。 根据辅助模块与被测试模块的关系, 辅助模块可以分为两类: 驱动模块是指调用被测试模块的虚模块,用来模拟被测试模块的上级模块。主要用来接受测试数据, 启动被测试模块,打印测试结果。 桩模块,又称为虚拟子程序。指被测试模块的调用模块,用来接受被测试模块的调用和输出数据。 被测试模块和与它相关的驱动模块和桩模块一起构成了 一个测试环境,如图7.7。 驱动模块、桩模块是单元测试的额外开销,在系统交付 时并不是实际运行系统的组成部分,但是它们可以构成被测 试模块的最小环境,使该模块的测试最可能的局部化,这对 于测试的可重复性和将来的维护是非常必要的。 2设计测试用例 根据逻辑覆盖标准和单元测试所需要解决的测试问题, 设计合适的测试用例。 3多个单元并行测试 为每一个模块设计相应的测试环境和测试用例,可以同时对多个模块进行测试。在进行代码执行活动的 动态测试时,代码审查组在研究设计文档的基础上,召开审查会,分析程序的逻辑与错误清单,并做相应的 单元测试报告。 在实际中,为了克服无法穷尽测试的实际困难,提高测试效率,在单元测试中应采用白盒法和黑盒法相 结合、静态测试与动态测试相结合、人工测试与计算机测试相结合的策略。 7.5 集成测试 单元测试结束后,需要把各个模块集成为一个系统。在组装的过程中,模块中隐藏的错误可能会出现, 还可能会出现一些新的错误,例如,数据穿过接口时可能丢失; 一个模块对另一个模块可能造成有害的影响; 把子功能组合起来可能得不到预期的效果; 个别模块看起来可以接受的误差可能积累到使人不能接受的程度; 全局数据结构可能会出现问题等等。 对模块进行组装时有两种方法,非增式组装测试和增式组装测试。非增式组装测试是先分别测试每个模 块,再把所有的模块按照设计要求一次性地组装起来。增式组装测试是把下一个要测试的模块同已经测试好 的那些模块结合起来进行测试, 测试完后再把下一个待测试的模块结合起来进行测试, 每次只增加一个模块。 7.5.1 非增式组装测试 这种方法将单元测试后的所有模块按照总体的结构图一次性的组装起来,然后对组装的整体进行测试, 得到最终要求的目标系统。一般采用黑盒法设计测试用例进行测试。 这种方法要求为每个模块设计驱动模块和桩模块,测试工作量大,而且只有在所有的模块单元测试完后 才能进行,可能进度难以保证。在测试的过程中,可能会出现程序错误爆炸现象,一次很难装配成功。而且 如果发现错误,也很难定位错误。这种方法只适合小规模的软件系统。 驱动模块 被测试模块 桩模块1桩模块n 测试用例测试结果 图7.7 单元测试的环境 7.5.2 增式组装测试 所谓增式组装测试是按照结构图自顶向下或自底向上逐渐把模块组装成一个系统,即安装一个模块测试 一个模块。增式组装测试在单元测试的基础上,采用自顶向下或自底向上逐层安装测试,直到测试结束。它 也可以采用自顶向下和自底向上相结合组装测试。测试后的模块可以作为当前要测试的模块的驱动/桩模块, 所以测试的编制工作量小,而且可以若干个子系统并行进行。采用这种方式可以将错误分解,容易找到错误 并容易测试成功,一般适合于大规模的软件系统。 这种方法的缺点是要求的测试时间较长,而且在测试时,已经测试的模块必须一起运行,运行开销大。 增式组装测试根据把待测试模块加入系统的方式可以分为两种方法: 1 自顶向下增式组装测试 按照结构图自顶向下组装进行测试,在测试的过程中只设计桩模块,不设计驱动模块。这种方式可在程 序测试的早期实现并验证系统的主要功能,及早发现上层模块的接口错误。自顶向下的组装方式有两种:宽 度优先组装和深度优先组装。 (1) 宽度优先组装 在向下组装的过程中根据宽度优先的原则逐层结合模块。这种方法的缺点是系统形成周期较长,用户不 能较早地看到系统功能。 (2) 深度优先组装 在实际中,为了尽早得到系统的局部功能,同时支持若干系统并行开发,一般采用深度优先的原则进行 模块结合。该方法使用户能较快地看到系统的关键子功能/子系统,能增强用户信息,保证系统的开发成功是 必要的。 例4:某系统的结构图如图 7.8 所示,按照宽度优先和深度优先组装的方式其组装图如图 7.9、图 7.10。 自顶向下模块结合的具体过程按照以下的 步骤进行: 第一步 对主模块进行测试, 测试中用桩模 块代替所有主模块的直接调用模块; 第二步 根据结合策略, 每次用一个实际模 块代替桩模块,同时又需要新的桩模块; 第三步 装入一个模块, 对它进行测。 当然 为了保证新加入的模块没有引入错误,需要对 以前测试过的模块进行回归测试。 自顶向下的结合在实施中可能碰到的问题 是高层模块要求底层模块的支持,对于数据流 从上到底,再逐层向上返回的情况,使用简单的桩模块不能满足要求。 2自底向上增式组装测试 按照结构图自底向上,逐步安装,逐步测试,直到测试成功。这个过程只设计驱动模块,不需要设计桩 模块。这种方式的缺点是使底层关键模块中的错误发现较晚,而且不能很快地在早期充分展开测试的人力。 自底向上增式组装测试的步骤如下: 第一步 使用驱动程序控制最底层模块进行测试; 第二步 用实际模块代替驱动模块,使其与已经测试的直接下级模块组装成新的子系统; 第三步 为子系统配置新的驱动模块,继续进行新的测试。反复这个过程,直到测试完成。 主模块 M1M2 M3M5M4 M7M6M8M9 图7.8 某系统结构图 图7.9 宽度优先组装图 图7.10 深度优先组装图 3两者相结合的组装测试 为了吸收两者的优点,提出了两者相结合的组装测试策略,对开发系统的上层用自顶向下的组装测试方 法,而对开发系统的中下层采用自底向上的组装测试方法,两者结合是一种好的折衷方案。 如果成功地执行了测试计划中规定的测试方案并且修改了错误,测试小组对测试结果进行了评审,那么 集成测试阶段基本结束。完成测试要求后,整理、分析测试结果,形成集成测试分析报告,记录测试所遇到 的错误、对错误的解决办法,对测试中已发现而没解决的问题,提出处理意见。 7.6 确认测试 对软件进行组装测试后, 软件系统已成为完整的软件包, 消除了接口的错误, 可以进行系统的确认测试, 验证系统是否满足需求规格说明书中规定的要求。确认测试所采用的技术以黑盒法为主。 确认测试主要由使用用户参加测试,测试的目的是向用户表明软件系统的有效性。根据软件需求说明书 中的描述,使用户能够确认软件的功能与性能同他们期望的一样。 在确认测试中,不是对系统具有的所有的功能进行测试。系统中为实现用户可见的功能和性能的后台纯 技术的功能和性能,在确认测试时可以不考虑。重点测试的是用户关心的系统可见的功能和性能。在进行测 试时所选择的测试用例中的数据,应该主要来自系统实际运行数据,因为用户对这些数据及预期的结果比较 熟悉,而且还应该设计并且执行一些与用户使用步骤相关的测试。 7.6.1 测试内容 根据需求规格说明书写出详细的测试计划,在验收测试时应该从以下几个方面进行测试。 (1) 系统功能测试:按照需求说明书的要求,测试系统是否具有用户所要求的功能; (2) 强度测试:加载所有负荷的情况下,运行系统,验证系统的负荷能力; (3) 性能测试;系统在实际运行环境中,是否具有需求说明书上所要求的系统性能; (4) 背景测试:系统在实际负荷情况下,测试系统运行多道程序、多道作业的能力; (5) 配置测试:在指定的逻辑组合或物理设备下,检查用户手册、操作手册、设计说明、源程序、测试 说明的完整性、可理解性、可用性与实际运行系统的一致性; (6) 恢复测试:测试系统在软件或硬件故障下,恢复原先控制数据的能力; (7) 安全测试:测试系统的安全性,测试系统能否使不合法用户不能进入系统。 7.6.2 测试步骤 在进行验收测试时,一般是按照如下的步骤进行的: (1)在模拟的环境中,首先进行强度测试(即压力测试) ,系统是否满足要求; (2)在计划的时间内,运行用户可见的软件功能,验证是否与用户的要求相符; (3)测试系统的配置、性能、背景、恢复能力、安全性是否满足要求,同时对软件的可移植性、兼容性、 可维护性做出评价; (4)测试过程中,详细记录不符合用户要求的内容及系统的缺陷; (5)测试结束,分析测试结果,找出产生错误的原因; (6)书写确认测试分析报告; (7)根据测试分析报告, 明确表示软件的功能及各种性能能否达到软件需求说明书的要求。 软件可以接受, 验收通过,或者被测试软件功能和性能与软件需求说明书的要求有一定的差距,不能通过,这时必须提交一 份报告,列出系统存在的问题和缺陷,分析产生这些错误的原因,必要时与用户进行协商,讨论解决问题的 办法。 (8)确认测试结束,如果验收通过,书写整个项目的开发总结报告。整理所用与系统相关的文件,并向 用户交付以下文件:用户手册、操作手册、维护阶段所需要的资料、项目开发总结报告。 7.7 软件调试 测试是发现软件外部错误的过程。对于发现的软件错误,找到其发生的原因和位置,即找到外部错误对 应的内部错误,进行纠正,直到测试没有错误为止,这就是调试。 调试包括两方面的内容:一方面的内容是诊断错误,找出错误的原因和位置,另一个方面是改正错误。 一般来说,知道错误的原因和位置,改正错误比较容易,而诊断错误比较难。通常诊断工作占据调试工作量 的90%以上。调试要求对程序结构和算法逻辑十分熟悉,一般由程序设计者本人进行。 7.7.1 软件调试方法 一般有如下几种方法: 1输出内存和寄存器的内容 采用十六进制和八进制码形式显示或者打印程序运行现场(内存与 CPU 寄存器组)的内 容。目的是直接观察、分析系统执行内码在出错时刻的运行状态。如果有可能,将系统运行断点设置到出错 前的某处,重新跟踪系统执行,从而找出错误位置,直接修改其中的错误。DUMP 是程序调试中最常用的技 术,它要求程序员非常熟悉系统的硬件和软件资源,能够干预系统环境。在机器码级直接修改程序可能是非 常危险的操作。应该在修改之前保存副本,以保证当修改失败时能够恢复到系统原有状态。 为保证系统安全, 有些系统运行环境禁止用户直接修改系统内码或者系统数据的内部表示。 一般情况下DUMP 是效率较低的调试办法,其主要缺点是: (1) 如果源程序采用高级语言书写,则很难将存储单元与程序变量对应; (2) DUMP 卸出的是程序在某一时刻的静止状态,而程序运行是一个动态过程; (3) 输出的运行现场内容可能不是程序出错时的状态; (4) 输出信息的形式不易阅读和理解。 2设置程序断点,插入打印语句 在程序中插入若干标准打印语句以输出某些变量的值,设置程序暂停控制。这种方法可以动态地显示关 键数据对象的行为,给出的信息容易与源程序对应。为程序员分析错误原因提供线索。这种方法比 DUMP 有效,但问题是: (1) 可能输出大量无关数据; (2) 必须修改源程序,这有可能改变关键的时间关系,从而掩盖错误或者导致新的错误。 3. 使用自动调试工具 目前许多程序设计语言的集成开发环境都提供程序调试功能,这包括不改变源程序代码利用调试运行功 能实现语句运行跟踪,以及程序断点设置、设置变量状态观察窗口、子程序调用序列跟踪等。 使用以上任何技术之前,都应该对于错误的征兆进行分析,通过分析得出故障的推测。否则将出现大量 无关信息。 7.7.2 常用调试策略 软件调试具有极强的技术性, 任何调试工具只是调试的辅助工具, 调试过程的关键不是使用上面的技术, 而是对错误的推测分析策略。调试过程中常用的分析策略有以下几种。 1. 跟踪法 在错误的征兆附近进行追踪,或是正向跟踪或是反向跟踪。所谓正向跟踪是指从可能的错误征兆处,沿 着控制流找到出错处,分析排除错误。所谓反向跟踪是从可能的错误征兆处逐步向后追溯,直到找准错误, 纠正为止。这种方法只适用于小规模的程序纠错,规模很大的程序,回溯路径太多,实际上无法进行。 2演绎法 从测试数据中分析可能出错的原因,排除不会发生的错误原因。分析余下的错误原因,可确定的,留下 继续分析,可确定就排除错误。剩余不可确定的原因,再增加测试数据,重复上述过程,直到排除错误。演 绎法是一个由普遍错误到特定错误的过程,是由一般到个别的分析排除过程。 3归纳法 归纳法纠错是由测试取得错误数据的个别数据,分析组织出一般可能的错误线索,研究出错的规律线索 关系,由此找出设置错误原因,证明设置错误原因,能证明就纠正错误,不能证明说明分析得不准,说明出 错的规律线索关系不正确,应再重新选取相应测试数据,如此周而复始地进行。可见归纳测试法是一个由特 殊到一般的错误推断排除法。简而言之,其过程是收集有关数据、组织数据、寻找假设、证明假设、排除假 设的错误的过程。 4试探法 针对错误不复杂,而程序又比较简单的程序,根据错误征兆,猜想出故障的大致位置,选取一种纠错方 法,找到有关的出错信息,借此逐渐确定原来的分析,渐渐找出错误的原因与位置,然后纠错。 5回溯法 纠错修改设计(或代码)的相应错误之后,必须重复执行先前测试集的过程称回溯测试,又叫回归测试, 目的在于进一步验证纠错的正确性。 6对分查找法 如果已经知道每个变量在程序内若干个关键点的正确值,则用赋值语句或输入语句在程序中的关键点附 近输入这些变量的正确值,然后检查程序的输出值。如果是正确的,则故障在程序的前半部分,反之故障在 后半部分。对程序中有故障的那部分再重复使用这个方法,直到把故障缩小到容易诊断的程度,定位错误并 纠错。 7.8 自动测试工具 软件测试是一项十分繁重艰苦的工作,据估计,测试工作量一般占软件总开发工作量的40%以上。为了 减轻测试工作难度,软件开发商需要借助于一些测试工具。现在市场上存在一些独立的软件测试工具。而比 较好的软件开发环境中也提供某些半自动化或

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论