第7章编码和测试7.1程序设计语言的选择•程序设计语言是人和计算机通信的最基本的工具,它的特点必然会影响人的思维和解题方式,会影响人和计算机通信的方式和质量,也会影响其他人阅读和理解程序的难易程度。因此,编码之前的一项重要工作就是选择一种适当的程序设计语言。•适宜的程序设计语言能使根据设计完成编码时所遇到的困难最少,可以减少需要的程序测试量,并且可以得出更容易阅读和更容易维护的程序。由于软件系统的绝大部分成本用在生命周期的测试盒维护阶段,所以容易测试盒容易维护时极端重要的。•使用汇编语言编码需要把软件设计翻译成机器操作的序列,由于这两种表示方法很不相同,因此汇编语言设计即困难又容易出现差错。一般来说高级语言的源程序语句和汇编代码指令之间有一句对多句的对应关系。但总的来说。高级语言明显优于汇编语言,因此,除了在很特殊的应用领域,或者大型系统中执行时间非常关键的•一小部分代码需要用汇编语言书写之外,其他程序应该一律运用高级语言书写。•为了使程序容易测试盒维护以减少软件的总成本,所选用的高级语言应该有理想的模块化机制,以及可读性好的控制结构和数据结构;为了便于调试和提高软件可靠性,语言特点应该使编译程序能够尽可能多地发现程序中的错误;为了降低软件开发和维护的成本,选用的高级语言应该有良好的独立编译机制。实用标准:•软件的应用领域•算法与计算的复杂性•数据结构的复杂性•效率•可移植性•程序设计人员的水平•构造系统的模式7.2编码风格•所谓编码风格即书写源程序的习惯、程序代码的逻辑结构与习惯的编程技术。7.2.1程序内部的文档所谓程序内部的文档包括恰当的标识符、适当的注释和程序的视觉组织等等。软件=程序+文档。为了提高程序的可维护性,源代码也需要实现“文档化”。内部文档的组织包括标识符命名、内部注释和程序的视觉组织。7.2.2数据说明原则•在设计阶段确定了数据结构的组织和复杂程度,编写程序时则要建立数据说明,使数据更容易理解,更容易维护。一般而言,数据说明应遵循3个原则:•(1)数据说明的次序应当规范化,使数据属性容易查找,有利于测试、排错和维护。•(2)当多个变量名用一个语句说明时,应当对这些变量按字母的顺序排列。•(3)如果涉及了一个复杂的数据结构,应当使用注释,说明这个数据结构的固有特点。7.2.3语句构造•涉及期间确定了软件的逻辑构造,然而个别语句的构造却是编写程序的一个主要任务。构造语句时应该遵循的原则是,每个语句都应该简单而直接,不能为了提高效率而使程序变得过分复杂。下述规则有助于使语句简单明了:•(1)不要为了节省空间而把多个语句写在同一行。•(2)尽量避免复杂的条件测试。•(3)尽量减少对非条件的测试。•(4)避免大量使用循环嵌套和条件嵌套。•(5)利用括号使逻辑表达式或算术表达式的运算次序清晰直观。7.2.4输入输出•在设计和编写程序时应该考虑下述有关输入输出风格的规则:•(1)对所有输入数据都进行检查。•(2)检查输入项重要组合的合法性。•(3)保持输入格式简单。•(4)使用数据结束标记,不要要求用户指定数据的数目。•(5)明确提示交互式输入的请求,详细说明可用的选择或边界数值。•(6)当程序设计语言对格式有严格要求时,应保持输入格式的一致。•(7)设计良好的输出报表。•(8)给所有输出数据加标志。7.2.5高效率原则•效率主要指的是处理机时间和存储容量两个方面。有3个原则:第一,效率是性能要求;第二,效率是靠好的设计来提高的;第三,程序的效率和程序的简单程度是一致的。•1、程序运行时间•在把详细设计结果翻译成程序时可以应用下述规则:•(1)写程序之前先简化算术和逻辑表达式。•(2)仔细研究嵌套的循环,以确定是否有语句可以从内层往外移。•(3)尽量避免使用多维数组。•(4)尽量避免使用指针和复杂的表。•(5)使用执行时间短的算术运算。•(6)不要混合使用不同的数据类型。•(7)尽量使用整数运算和布尔表达式。•在效率是决定性因素的应用领域,尽量使用有良好优化特性的编译程序,以自动生成高效目标代码。2、存储器效率•在大型计算机中必须考虑操作系统页式调度的特点,一般来说,使用能保持功能域的结构化控制结构,是提高效率的较好方法。•在微处理器中如果要求使用最少的存储单元,则应选用有紧缩存储器特性的编译程序,在非常必要时可以使用汇编语言。•提高执行效率的技术通常也能提高存储器效率。提高存储器效率的关键同样是“简单”。3、输入输出的效率•如果用户为了给计算机提供输入信息或为了理解计算机输出的信息,所需花费的脑力劳动是经济的,那么人和计算机之间通信的效率就高。因此,简单清晰同样是提高人机通信效率的关键。•硬件之间的通信效率是很复杂的问题,但是,从写程序的角度看,却有些简单的原则可以提高输入输出的效率。7.3软件测试•7.3.1软件测试目标•软件测试,是为了发现错误而执行程序的过程。或者说,软件测绘是根据软件开发各阶段的规格说明和程序的内部结构而精心设计一批测试用例,并利用这些测试用例去运行程序,以发现程序错误的过程。软件测试在软件生存周期中横跨两阶段:通常在编写出每个模块之后对它做出必要•的测试(称为单元测试)。在这个阶段结束之后,软件系统还要进行各种综合测试,这是软件生存周期的另一个独立的阶段,即测试阶段,通常由专门的测试人员承担这项工作。•软件测试是对需求分析、设计和编码3个阶段进行最终的复审。•软件测试不是为了发现程序没有错误,也不是为了表明程序时正确的。软件测试的根本目标是尽可能多地发现错误。测试目标决定了测试方案的设计。•测试阶段的根本目标是尽可能多地发现并排除软件中潜藏的错误,最终把一个高质量的软件系统交给用户使用。但是,如果仅就测试本身而言,软件测试的目标是以最小的时间和人力发现软件中潜在的各种错误和缺陷。测试只能查找程序中的错误,不能证明程序中没有错误。7.3.2软件测试准则•为了能设计出有效的测试方案,软件工程师必须深入理解并正确运用指导软件测试的基本准则。下面讲述主要的测试准则。•(1)所有测试都应该能追溯到用户需求。•(2)应该远在测试开始之前就制定出测试计划。•(3)把Pareto原理应用到软件测试中。•(4)应该从“小规模”测试开始,并逐步进行“大规模”测试。•(5)穷举测试时不可能的。•(6)为了达到最佳的测试效果,应该由独立的第三方从事测试工作。7.3.3测试方法•测试任何产品都有两种方法:如果已经知道了产品应该具有的功能,可以通过测试来检验是否每个功能都能正常使用;如果知道产品的内部工作过程,可以通过测试来检验产品内部动作是否按照规格说明书的规定正常运行。前一种方法称为黑盒测试,后一种方法称为白盒测试。•黑盒测试又称为功能测试。白盒测试又称为结构测试。•黑盒测试:基于程序外部功能而进行的测试,又称功能测试。•白盒测试:基于程序内部结构而进行的测试,又称结构测试。7.3.4测试步骤•大型软件系统的测试过程基本由下述几个步骤组成:•1、模块测试•2、子系统测试•3、系统测试•4、验收测试•5、平行运行7.3.5测试阶段的信息流•图7-1描绘了测试阶段的信息流,测试过程需要三类输入:a、软件配置。b、测试配置;c、测试工具。•测试后进行结构分析,即将实测结构与预期的结果比较,如发现错误就进行排错。排错也就是调试,即对发现的错误进行错误定位,确定出错性质,改正这些错误,并修正相关的文档。修正的文档一般要经过再次测试,直到通过测试为止。•通过收集和分析测试结果中出错率数据可建立可靠性模型,进行可靠性预报。如果经常出现修改设计的严重错误,那么软件质量和可靠性就不能保证,应对软件进一步测试。如果经测试,软件功能完善,错误率数据很少,并易于修改,那么则有两种可能:要么是软件的质量和可靠性可以接受,要么是所做的测试不充分。7.4单元测试•单元测试集中监测软件设计的最小模块。通常,单元测试和编码属于软件过程的同一个阶段。可以应用人工测试和计算机测试两种不同类型的测试方法,完成单元测试工作。单元测试主要使用白盒测试技术,而且对多个模块的测试可以并行地进行。7.4.1测试重点•1、模块接口•2、局部数据结构•3、重要的执行通路•4、出错处理通路•5、边界条件1、模块接口•首先应该对通过模块接口的数据流进行测试,如果数据不能正确地进行,所有其他测试都是不切实际的。•在对模块接口进行测试时主要检查下述几个方面:参数的数目、次序、属性或单位系统与变元是否一致;是否修改了只作输入用的变元;全局变量的定义和用法在各个模块中是否一致。2、局部数据结构•对于模块来说,局部数据结构是常见的错误来源。应该仔细设计测试方案,以便发现局部数据说明、初始化、默认值等方面的错误。3、重要的执行通路•由于通常不可能进行穷举测试,因此,在单元测试期间选择最有代表性、最可能发现错误的执行通路进行测试就是十分关键的。应该设计测试方案用来发现由于错误的计算、不正确的比较或不适当的控制流而造成的错误。4、出错处理通路•好的设计应该能够预见出现错误的条件,并且设置出适当的处理错误的通路,以便在真的出现错误时执行相应的出错处理通路或干净地结束处理。不仅应该在程序中包含出错处理通路,而且应该认真测试这种通路。当评价出错处理通路时,应该着重测试下述一些可能发生的错误:4、出错处理通路•(1)对错误的描述是难以理解的;•(2)记下的错误与实际遇到的错误不同;•(3)在对错误进行处理之前,错误条件已经引起系统干预;•(4)对错误的处理不正确;•(5)描述错误的信息不足以帮助确定造成错误的位置。5、边界条件•边界测试时单元测试中最后的也可能是最重要的任务。软件常常在它的边界上失效,例如,处理n个元素时,或做到i次重复时,往往会发生错误。使用刚好小于、刚好等于和刚好大于最大值或最小值的数据结构、控制量和数据值的测试方案,最容易发现软件中的错误。7.4.2代码审查•人工测试源程序可以由编写者本人非正式地进行,也可以由审查小组正式进行。后者成为代码审查,它是一种非常有效的程序验证技术,对于典型的程序来说,可以查出30%--70%的逻辑设计错误和编码错误。审查小组最好又下述4人组成:a、组长,应该是一个很有能力的程序员,而且没有直接参与这项工程;b、程序的设计者;c、程序的编写者;d、程序的测试者。•审查之前,小组成员应该先研究设计说明书,力求理解这个设计。为了帮助理解,可以先由设计者扼要地介绍他的设计。在审查会上由程序的编写者解释它是怎样用程序代码实现这个设计的,通常是逐个语句地讲述程序的逻辑,小组其他成员仔细倾听他的讲解,并力图发现其中的错误。审查会上进行的另外一项工作,是对照类似于上一小节中介绍的程序设计常见错误清单,分析审查这个程序。当发现错误时由组长记录下来,审查会继续进行。•审查会还有另外一种常见的进行方法,称为预排:由一个人扮演“测试者”,其他人扮演“计算机”。会前测试者准备好测试方案,会上由扮演计算机的成员模拟计算机执行被测试的程序。代码审查比计算机测试的优越之处是:•一次审查会上可以发现许多错误;用计算机测试的方法发现错误之后,通常需要先改正这个错误才能继续测试,因此错误时逐个地发现并改正的。也就是说,采用代码审查的方法可以减少系统验证的总工作量。•实践表明,对于查找某些类型的错误来说,人工测试比计算机测试更为有效;对于其他类型的错误来说刚刚好相反。因此,人工测试盒计算机测试时互相补充,相辅相成的,缺少其中任何一种方法都会使查找错误的效率降低。7.4.3计算机测试•模块并不是一个独立的程序,因此必须为每个单元测试开发驱动软件和存根软件。通常驱动程序也就是一个“主程序”,它接收测试数据,把这些数据传送给被测试的模块,并且印出有关的结构。存根程序代替被测试的模块所调用的模块。因此存根程序也可以称为“虚拟子程序”。它使用被它代替的模块的接口,可能做最少量的数据操作,印出对入口的检验或操作结果,并且把控制归还给调用它的模块。7.5集成测试•集成测试时测试盒组装软件的系统化技术。•由模块组装成程序时有两种方法:(1)非渐增式测试方法先分别测试每个模块,再把