第5章结构化实现所谓编码就是把软件设计翻译成计算机可以理解的形式——用某种程序设计语言书写的程序。同软件设计一样,软件编码风格也会对程序的可靠性、可读性、可测试性和可维护性产生深远的影响。无论怎样强调软件测试的重要性和它对软件可靠性的影响都不过分。测试的目的就是在软件投入生产性运行之前,尽可能多地发现软件中的错误。测试是对软件规格说明、设计和编码的最后复审。仅就测试而言,它的目标是发现软件中的错误,但是,发现错误并不是我们的最终目的。软件工程的根本目标是开发出高质量的完全符合用户需要的软件,因此,通过测试发现错误之后还必须诊断并改正错误,这就是调试的目的。调试是测试阶段最困难的工作。编码5.1软件测试基础5.2逻辑覆盖5.3控制结构测试5.4墨盒测试技术5.5测试策略5.6调试5.7软件可靠性5.8小结5.95.1编码5.1.1选择程序设计语言程序设计语言是人和计算机通信的基本工具。因此,编码之前的一项重要工作就是选择一种适当的程序设计语言。总的来说,高级语言明显优于汇编语言。用高级语言编写的程序容易阅读,容易测试,容易调试,容易维护。注意:1初学者不要妄言一种语言的好坏。参看《学习c++的50规则》2学习方法学习程度的划分:陌生、了解、熟悉、掌握、精通、大师5.1.2编码风格源程序代码的逻辑简明清晰、易读易懂是好程序的一个重要标准。为了做到这一点,有一些应该遵循的规则。参看《华为编程规范5.2软件测试基础表面上:软件工程的其他阶段都是“建设性”的软件工程的测试阶段却是“破坏性”的实质上:测试阶段的根本目标是尽可能多地发现并排除软件中潜藏的错误,最终把一个高质量的软件系统交给用户使用5.2.1测试目标G.Myers给出了关于测试的一些规则,这些规则也可以看作是测试的目标或定义:·测试是为了发现程序中的错误而执行程序的过程·好的测试方案是极可能发现迄今为止尚未发现的·成功的测试是发现了至今为止尚未发现的错误的测试此外,应该认识到测试决不能证明程序是正确的。测试只能查找出程序中的错误,不能证明程序中没有错误。5.2.2黑盒测试和白盒测试黑盒测试法把程序看成一个黑盒子,完全不考虑程序的内部结构和处理过程。也就是说,黑盒测试是在程序接口进行的测试,它只检查程序功能是否能按照规格说明书的规定正常使用,程序是否能适当地接收输入数据产生正确的输出信息,并且保持外部信息的完整性。黑盒测试又称为功能测试。与黑盒测试法相反,白盒测试法的前提是可以把程序看成装在一个透明的白盒子里,也就是完全了解程序的结构和处理过程。这种方法按照程序内部的逻辑测试程序,检验程序中的每条通路是否都能按预定要求正确工作。白盒测试又称为结构测试。5.2.3为了能设计出有效的测试方案,软件工程师必须充分理解并正确运用指导软件测试的基本准则。主要的测试准则如下所述:·所有的测试都应该能追溯到用户需求。·应该在测试开始之前的相当长时间,就制定出测试计划。·把Pareto原理应用于软件测试。Pareto原理告诉我们,测试发现的错误中的80%很可能是由程序中20%的模块造成的。·测试应该从“小规模”开始,并逐步进行“大规模”测试。·穷举测试是不可能的。·为了达到最佳的测试效果,应该由独立的第三方来从事测试工作。5.2.4设计测试方案时,往往需要仔细分析程序的控制流。为了突出表示程序的控制流,可以使用流图(也称为程序图)。流图仅仅描绘程序的控制流程,它完全不表现对数据的具体操作以及分支或循环的具体条件。与状态转换图的区别:状态转换图主要针对要开发系统的全局,而流图则主要描述某一功能的具体细节。在流图中用圆表示节点,一个圆代表一条或多条语句。程序流程图中的一个处理框序列和一个菱形判定框,可以映射成流图中的一个节点。流图中的箭头线称为边,它和程序流程图中的箭头线类似,代表控制流。注意:在流图中一条边必须终止于一个节点,即使这个节点并不代表任何语句(实际上相当于一个空语句)。由边和节点围成的面积称为区域,当计算区域数时应该包括图外部未被围起来的那个区域。合条件分解为若干个简单条件,每个简单条件对应流图中一个节点。包含条件的节点称为判定节点。图5.1举例说明把程序流程图映射成图5.1(a)程序流程图;(b)流图procedure:sort1:dowhilerecordsremainreadrecord;2:ifrecordfield1=03:thenprocessrecord;storeinbufferincremertcounter;4:elseifrecardfield2=05:thenresetcounter;6:elseprocessrecord;storeinfile;7a:endifendif7b:enddo8:end图5.2由PDL图5.3由包含复合条件的PDL映射成的流图5.3逻辑覆盖测试方案:包括具体的测试目的(例如,要测试的具体功能),应该输入的测试数据和预期的输出结果。设计测试方案是测试阶段的关键技术问题。通常把测试数据和预期的输出结果称为测试用例。注意:不同的测试数据发现程序错误的能力差别很大,为了提高测试效率降低测试成本,应该选用高效的测试数据。有选择地执行程序中某些最有代表性的通路是对穷尽测试的唯一可行的替代办法。所谓逻辑覆盖是对一系列测试过程的总称,这组测试过程逐渐进行越来越完整的通路测试。逻辑覆盖是设计白盒测试方案的一种技术。测试数据覆盖程序逻辑的程度可以划分成哪些不同的等级,从覆盖源程序语句的详尽程度分析,大致有以下一些不同的覆盖标准。1.语句覆盖的含义是,选择足够多的测试数据,使被测程序中每个语句至少执行一次。总结:语句覆盖只关心判定表达式的值,而没有分别测试判定表达式中每个条件取不同值时的情况。图5.4被测试模块的流程图2.判定覆盖又叫分支覆盖,它的含义是,不仅每个语句必须至少执行一次,而且每个判定的每种可能的结果都应该至少执行一次,也就是每个判定的每个分支都至少执思考:为什么说判定覆盖只覆盖了程序全部路径的一半?3.条件覆盖条件覆盖的含义是,不仅每个语句至少执行一次,而且使判定表达式中的每个条件都取到各种可能的结果。注意:判定覆盖不一定包含条件覆盖,条件覆盖也不一定包含判定覆盖!4.判定/既然判定覆盖不一定包含条件覆盖,条件覆盖也不一定包含判定覆盖,自然会提出一种能同时满足这两种覆盖标准的逻辑覆盖,这就是判定/条件覆盖。它的含义是,选取足够多的测试数据,使得判定表达式中的每个条件都取到各种可能的值,而且每个判定表达式也都取到各种可能的结果。5.条件组合覆盖是更强的逻辑覆盖标准,它要求选取足够多的测试数据,使得每个判定表达式中条件的各种可能组合都至少出现一次。条件覆盖、判定/条件覆盖,但不一定覆盖程序的全部路径。5.4控制结构测试5.4.1基本路径测试是TomMcCabe提出的一种白盒测试技术。使用这种技术设计测试用例时,首先计算过程设计结果的逻辑复杂度,并以该复杂度为指南定义执行路径的基本集合,从该基本集合导出的测试用例可以保证程序中的每条语句至少执行一次,而且每个条件在执行时都将分别取true(真)和false(假)使用基本路径测试技术设计测试用例1.根据过程设计结果画出相应的流图图5.5求平均值过程的流图1:i=1total,input=totalvalid=0sum=02:DOWHILEvalue〔i〕<>-9993:ANDtotalinput<1004:incrementtotalinputby1;5:IFvalue〔i〕>=minimum6:ANDvalue〔i〕<=maximum7:THENincrementtotalvalidby1;sum=sum+value〔i〕;8:ENDIFincrementiby1;9:ENDDO10:IFtotalvalid>011:THENaverage=sum/totalvalid;12:ELSEaverage=-99913:ENDIF2.用环形复杂度来定量度量程序的逻辑复杂性。有了描绘程序控制流的流图之后,可以用下述三种方法之一来计算环形复杂·流图中的区域数等于环形复杂度。·流图G的环形复杂度V(G)=E-N+2,其中E是流图中边的条数,N是流图中节点数。·流图G的环形复杂度V(G)=P+1,其中P是流图中判定使用上述任何一种方法,都可以计算出图5.5所示流图的环形复杂度为6。3.确定线性独立路径的基本集合所谓独立路径,是指至少引入程序的一个新处理语句集合或一个新条件的路径,用流图术语描述,独立路径至少包含一条在定义该路径之前不曾用过的边。首先标注出判定节点。对于图5.5所描述的求平均值过程来说,由于环形复杂度为6,因此共有6条独立路径。例如,下面列出了6路径1:1-2-10-11-13路径2:1-2-10-12-13路径3:1-2-3-10-11-13路径4:1-2-3-4-5-8-9-2-路径5:1-2-3-4-5-6-8-9-2-路径6:1-2-3-4-5-6-7-8-9-2-路径4、5、6后面的省略号(...),表示可以后接通过控制结构其余部分的任意路径(例如,10-11-13)。4.设计可强制执行基本集合中每条路径的测试用例应该选取数据,使得在测试每条路径时都适当地设置好了各个判定节点的条件。应该注意,某些独立路径不能以独立的方式测试,在这种情况下,这些路径必须作为另一个路径的一部分来测试。路径1Value〔k〕=有效输入值,其中k<i(i的定义在下面)value〔i〕=-999,其中2≤i≤100预期结果:基于k的正确平均值和总数注意,路径1无法独立测试,必须作为路径4、5和6路径2value〔1〕=-999预期结果:average=-999,其他都保路径5value〔i〕=有效输入值,其中i<100value〔k〕>maximum,其中k<I预期结果:其于k的正确平均值和总数路径6value〔i〕=有效输入值,其中i<100预期结果:正确的平均值和总数5.4.2用条件测试技术设计出的测试用例,能够检查程序模块中包含的逻辑条件。一个简单条件是一个布尔变量或一个关系表达式,在布尔变量或关系表达式之前还可能有一个NOT(“┐”)算符。关系表达式的形式如下:E1<关系算符>E2其中,E1和E2是算术表达式,而<关系算符>是下列算符之一:“<”,“≤”,“=”,“≠”,“>”或“≥”。复合条件由两个或多个简单条件、布尔算符和括弧组成。布尔算符有OR(“|”),AND(“&”)和NOT(“┐”)。不包含关系表达式的条件称为布尔表达式。在上述种种条件测试技术的基础上,K.C.Tai提出了一种被称为BRO(BranchandRelationalOperalor)测试的条件测试策略。BRO测试利用条件C的条件约束来设计测试用例。包含n个简单条件的条件C的条件约束定义为(D1,D2,…,Dn),其中Di(0<i≤n)表示条件C中第i个简单条件的输出约束。如果在条件C的一次执行过程中,C中每个简单条件的输出都满足D中对应的约束,则称C的这次执行覆盖了C的条件约束D对于布尔变量B来说,B的输出约束指出,B必须是真(t)或假(f)。类似地,对于关系表达式来说,用符号>,=和<指定C1:B1&B2其中,B1和B2是布尔变量。C1的条件约束形式为(D1,D2),其中D1和D2中的每一个都是“t”或“f”。值(t,f)是C1的一个条件约束,并由使B1值为真B2值为假的测试所覆盖。BRO测试策略要求,约束集{(t,t),(f,t),(t,f)}被C1的执行所覆盖。如果C1因布尔算符错误而不正确,则至少上述约束集中的一个约束将迫使C1失5.4.3数据流测试方法根据程序中变量定义和使用的位置,选择程序的测试路径。为了说明数据流测试方法,假设已赋予程序每条语句一个唯一的语句号,而且每个函数都不修改它的参数或全局变量。对于语句号为SDEF(S)={X|语句S包含变量X的定义}USE(S)={X|语句S使用变量X变量X的定义——使用链(或称为DU链)的形式为〔X,S,S′〕,其中S和S′是语句号,X在集合DEF(S)