1、单元测试学时:4学时 6.1 6.1 单元测试的目标及内容单元测试的目标及内容指对软件中的指对软件中的最小可测试单元最小可测试单元或或基本组成单元基本组成单元进进行检查和验证。行检查和验证。单元测试的定义:单元测试的定义:6.1 6.1 单元测试的目标及内容单元测试的目标及内容单元选取原则单元选取原则面向过程面向过程面向对象面向对象图形化软件图形化软件函数或子过程函数或子过程类类窗口或菜单窗口或菜单6.1.1 单元测试的目标(1)单元测试就是为了检测代码单元的逻辑功能,找出单元本身的所有功能逻辑错误。进一步说,就是检测对数据的各种分类是否考虑全面,处理是否正确。单元测试并不是用来代替系统测试、
2、性能测试的,它的目标很明确,就是检测代码单元的功能逻辑错误。单元测试的目的除了要发现编码中引入的错误和发现代码和周详设计不一致的地方之外,也是为了确保周详设计的质量。6.1.1 单元测试的目标(2)单元测试需要达到以下一些具体目标:信息能否正确地流入和流出单元。在单元工作过程中,其内部数据能否保持完整性,包括内部数据的形式、内容及相互关系不发生错误,也包括全局变量在单元中的处理和影响。在为限制数据加工而设置的边界处,能否正确工作。单元的运行能否做到满足特定的逻辑覆盖。单元中发生了错误,其中的出错处理措施是否有效。6.1.1 单元测试的内容(1)单元测试的内容有以下几方面:1)模块接口测试对模块
3、接口,包括参数表、调用子模块的输入参数是否正确、全程数据、此模块调用子模块时的输出参数是否正确、是否修改了只读型参数、全局变量的定义在各模块中是否一致、文件输入/输出操作都必须检查。6.1.1 单元测试的内容(2)2)局部数据结构测试设计测试用例检查数据类型说明、初始化、缺省值等方面的问题,还要查清全程数据对模块的影响。检查局部数据结构是为了保证临时存储在模块内的数据在执行过程中的完整、正确。局部数据结构往往是错误的根源,应仔细设计测试用例,力求发现以下错误:不正确或不一致的数据类型说明、使用尚未赋值或者尚未初始化的变量、错误的初始值或缺省值、变量名的拼写或书写错误、溢出或地址异常。6.1.1
4、 单元测试的内容(3)3)路径测试选择适当的测试用例,对模块中重要的执行路径进行测试。对基本执行路径和循环进行测试可以发现大量路径错误。进行路径设置时,设置测试用例是为了发现因错误计算、不正确的比较和不适当的控制流造成的错误。4)错误处理测试检查模块的错误处理功能是否包含有错误或缺陷。例如,是否拒绝不合理的输入,出错的描述是否难以理解、是否对错误定位有误、是否出错原因报告有误、是否对错误条件的处理不正确,在对错误处理之前错误条件是否已经引起系统的干预等。6.1.1 单元测试的内容(4)3)路径测试选择适当的测试用例,对模块中重要的执行路径进行测试。对基本执行路径和循环进行测试可以发现大量路径错
5、误。进行路径设置时,设置测试用例是为了发现因错误计算、不正确的比较和不适当的控制流造成的错误。4)错误处理测试检查模块的错误处理功能是否包含有错误或缺陷。例如,是否拒绝不合理的输入,出错的描述是否难以理解、是否对错误定位有误、是否出错原因报告有误、是否对错误条件的处理不正确,在对错误处理之前错误条件是否已经引起系统的干预等。6.1.1 单元测试的内容(5)5)边界测试要特别注意数据流、控制流中刚好等于、大于或小于确定的比较值时出错的可能性。对这些地方要仔细地选择测试用例,认真加以测试。边界测试是单元测试的最后,也是最重要的一项。此外,如果对模块运行时间有要求的话,还要专门进行关键路径测试,以确
6、定最坏情况下和平均意义下影响模块运行时间的因素。代码的功能逻辑越复杂,即算法密集度越高,可能出现的错误就越多,越需要全面的测试,测试效益越大。所以,如果时间紧张,应该优先测试功能逻辑复杂的代码。80-20规则在软件开发中同样存在,也就是说80%的代码错误可能存在于20%的代码中,20%的代码可能占用了80%的开发调试时间。这20%就是功能逻辑复杂的代码,即使只对这些代码进行单元测试,也能产生巨大的效益。6.1.1 单元测试的内容(6)6.1.1 单元测试的内容(7)单元测试是针对代码单元的独立测试。“独立”是指将代码从原始项目及其依赖的环境中隔离出来,针对各个单元单独进行测试,包括3方面含义:
7、形式独立、实质独立和意识独立。单元测试之所以困难,是因为技术上无法解决形式独立和实质独立,主观上没有达到意识独立。1)形式独立将测试任务从原始项目及其依赖环境中隔离出来,并能在易于测试的环境下运行。企业项目通常高耦合、可测性差、依赖于特定的软件环境或硬件环境,单元测试要首先将测试任务从依赖的代码和环境中分离出来,并解决编译、平台差异等问题,使测试任务在易于测试的环境下能够独立测试。6.1.1 单元测试的内容(8)代码的功能逻辑越复杂,即算法密集度越高,可能出现的错误就越多,越需要全面的测试,测试效益越大。所以,如果时间紧张,应该优先测试功能逻辑复杂的代码。80-20规则在软件开发中同样存在,也
8、就是说80%的代码错误可能存在于20%的代码中,20%的代码可能占用了80%的开发调试时间。这20%就是功能逻辑复杂的代码,即使只对这些代码进行单元测试,也能产生巨大的效益。6.1.1 单元测试的内容(9)2)实质独立被测试代码通常会调用各种底层函数,其功能逻辑难免受底层函数的影响,如何独立完整地进行测试?实际上,调用底层函数获得的数据,与通过参数传递的数据具有同等意义,例如,调用底层函数得到一个返回值,要测试的是被测程序对这个值的各种可能有没有做合适的判断处理,并不关心底层函数如何计算这个值,正如只关心是否对参数的各种可能做了合适处理,而不关心参数是如何传递和由谁传递一样。因此,调用底层函数
9、获得的数据可视为被测函数的一种输入,称为内部输入。如果可以模拟底层函数的各种输出,并将这些输出视为被测函数的内部输入,那么,无论底层函数属于哪种情形,都可以完整测试被测代码的功能逻辑,这就是实质独立。6.1.1 单元测试的内容(10)内部输入分为易于自然取得(调用真实的底层函数获得)、不可控(调用真实底层函数但其行为难于控制)、失真(打桩的必然后果)、难于自然取得等4种情形。易于自然取得的内部输入并不影响被测函数的独立性,而后3种则是单元测试的关键难点。静态局部变量也是麻烦的内部输入。3)意识独立意识独立是主观要素,是指在制定测试目标和执行测试时,要适应“单元测试是针对代码单元的独立测试”这一
10、基本现实,把测试任务看做是一个一个的独立单元,正确设定测试目标、正确选择测试方法及工具。6.1.1 单元测试的内容(11)单元测试有独特的优势,例如易于完整地测试代码单元的功能逻辑,这些优势来自“独立”,但凡事有其长必有其短,既然是“独立”,就难以测试与其他代码和依赖环境的相互关系。单元测试与系统测试是互补关系,不是代替关系,“独立”状态下易于发现的错误,才是单元测试的目标;集成后才易于发现的问题,应该留待系统测试完成。代码单元本身的功能逻辑错误都是单元测试的目标,而性能问题(时间性能如执行速度,空间性能如存储空间大小、内存泄漏)难以在最小单元内测试,不是单元测试目标。6.1.1 单元测试的内
11、容(12)编码规范检查与单元测试无关,无论是否实施单元测试,编码规范检查都是必不可少的工作。静态分析属于全局扫描,严格来说也不是单元测试,提高编译器的警告级别,就是最简单高效的静态分析。单元测试是意义重大且困难的工作,目标应该具体而明确,将不属于单元测试或单元测试不擅长的目标牵扯进来,其结果往往是“拣了芝麻,丢了西瓜”。6.1.1 单元测试的内容(13)6.2 单元测试的环境 模块并不是一个独立的程序,在考虑测试模块时,同时要考虑它和外界的联系,用一些辅助模块去模拟与被测模块相联系的其他模块。这些辅助模块分为两种:驱动模块和桩模块。被测模块、与它相关的驱动模块和桩模块共同构成了测试环境。6.2
12、.1 驱动模块和桩模块的定义驱动模块和桩模块是两个比较重要的概念。(1)驱动模块:相当于被测模块的主程序,它接收测试数据,把这些数据传送给被测模块,启用被测模块,最后输出实测结果。主模块作为驱动模块,与之直接相连的模块用桩模块代替。(2)桩模块:集成测试前要为被测模块编制一些模拟其下级模块功能的“替身”模块,以代替被测模块的接口,接受或传递被测模块的数据,这些专供测试用的“假”模块称为被测模块的桩模块。6.2.1 驱动模块和桩模块的定义 桩模块用来代替所测的子模块,它不能为空,也并不 需要子模块的所有功能都实现,可以只实现一部分;驱动模块用来代替主模块,用它来调用子模块。简单地说,被测模块上层
13、为驱动模块,是调用被测模块的,被测模块下层为桩模块,是被被测模块调用的(驱动模块和桩模块的举例将在6.2.3小节中具体说明)。驱动模块和桩模块为程序单元的执行构成了一个完整的环境。驱动模块用以模拟被测单元的上层模块,测试执行时由驱动模块调用被测单元使其运行,桩模块模拟被测单元执行过程中所调用的模块,测试执行时桩模块使被测单元能完整闭合地运行。6.2.1 驱动模块和桩模块的定义6.2.2 驱动模块和桩模块的使用条件 一般认为单元测试应紧接在编码之后,当源程序编制完成并通过复审和编译检查,便可开始单元测试。测试用例的设计应与复审工作相结合,根据设计信息选取测试数据,将增大发现错误的可能性。在确定测
14、试用例的同时,应给出期望结果。此时,应为测试模块开发一个驱动模块(driver)和(或)若干个桩模块(stub)。驱动模块在大多数场合称为“主程序”,它接收测试数据并将这些数据传递到被测试模块,被测试模块被调用后,“主程序”打印“进入退出”消息。实施单元测试的最佳时机是在编码阶段,若编码阶段没有进行单元测试,则也可在后期单独开发单元测试程序。驱动模块和桩模块是测试使用的软件,而不是软件产品的组成部分,但它需要一定的开发费用。若驱动和桩模块比较简单,实际开销相对低些。6.2.2 驱动模块和桩模块的使用条件 按照TDD的思想,自然是要先编写单元测试,然后再编写能够通过该单元测试的方法。在不实践TD
15、D的项目中,事后编写单元测试仍有其合理性:(1)以消极的态度来看,既然项目本身不严格要求事先编写单元测试,那么就可以在事后去做了。(2)事后编写单元测试至少也是一种检验手段,当然,肯定比不上事先编写的单元测试。因为,事后编写的单元测试很可能会“将就”已经写好的应用程序,事后编写的单元测试将是肤浅的,不会对代码进行良好的测试。6.2.2 驱动模块和桩模块的使用条件(3)可以把单元测试(其中包含事后单元测试)作为“后来者”了解、学习应用程序的手段。因为单元测试程序就是应用程序的“客户”,所以无论它是事先写的,还是事后写的,都可以很好地表现出应用程序的行为。(4)事后单元测试,也可能转化为事先单元测
16、试。在应用程序的整个生命周期中,维护阶段是最长的。在“漫长”的维护过程中,“之前”所写的“事后”单元测试将会成为“后来者”(包括原始作者本人)的“事先”单元测试。在改进程序的过程中,这些单元测试仍然能起到监督的作用。6.2.3 驱动模块和桩模块的设计驱动模块和桩模块的概念和设计我们将通过一个简单的例子来说明。现在假设一个程序由A、B、C、D、E、F六个模块组成,结构下图所示。6.2.3 驱动模块和桩模块的设计假设现在项目组把任务分给了6个人上面只有六个模块,那又如何实现每个人负责实现一个模块?,每个人负责实现一个模块。你负责的是B模块,你很优秀,第一个完成了编码工作,现在需要开展单元测试工作,
17、先分析结构图:(1)由于B模块不是最顶层模块,所以它一定不包含main函数(A模块包含main函数),也就不能独立运行。(2)B模块调用了D模块和E模块,而目前D模块和E模块都还没有开发好,因此想让B模块通过编译器的编译也是不可能的。6.2.3 驱动模块和桩模块的设计那么怎样才能测试B模块呢?方法如下:(1)写两个模块Sd和Se分别代替D模块和E模块(函数名、返回值、传递的参数相同),这样B模块就可以通过编译了。Sd模块和Se模块就是桩模块。(2)写一个模块Da用来代替A模块,里面包含main函数,可以在main函数中调用B模块,让B模块运行起来。Da模块就是驱动模块。桩模块的使命除了使得程序
18、能够编译通过之外,还需要模拟返回被代替的模块的各种可能返回值(什么时候返回什么值需要根据测试用例的情况来决定)。驱动模块的使命就是根据测试用例的设计去调用被测试模块,并且判断被测试模块的返回值是否与测试用例的预期结果相符。6.2.3 驱动模块和桩模块的设计2桩模块的设计桩模块主要完成以下内容:(1)接受测试输出。(2)将输出传给桩模块。(3)接受被测单元执行结果,并对结果进行判断。(4)将判断结果作为用例执行结果输出测试报告。6.3 单元测试的策略 单元测试是软件测试的基础,单元测试跟软件设计一样,有一些常用的策略模式。将若干个模块连接成一个可运行的系统通常有两种方式:一种是“非渐增式”,即先
19、独立地测试每一模块,然后将所有这些模块连接到一起运行;另一种是“渐增式”,即在已测试过的N个模块的基础上再增加一个模块,再对N+1个模块进行测试。左图为 X的非渐增式测试;右图为B的非渐增式测试。6.3 单元测试的策略6.3 单元测试的策略非渐增式是先分别测试6个模块A,B,C,D,E,F,然后将6个模块连接到一起再进行测试。若用这种方式,在测试某个模块X时,需要为它设计一个驱动模块和若干个桩模块,如图6-5所示。驱动模块的作用是模拟X的调用模块,桩模块的作用是模拟X的下层模块。例如测试如图6-6所示的模块B时,要为它设计一个驱动模块,其作用是将测试数据传送给模块B,并显示B产生的结果,另外,
20、由于模块B要调用模块E,所以还需设计一个名字为E的模块,它将接受B的控制并模拟E的功能。由图6-6可知,模块B要调用的是模块D,而非模块E,请修改这里。(这里不用修改了,图片画错了,文字的对的)另一种方式是渐增式,它不是分别测试每个模块,而是逐步将要测试的模块同已测试的模块连接起来。若用渐增方式,模块测试和联合测试这两步是结合起来进行的。渐增式又包含有由上而下、自下而上等。6.3.1.自上而下的测试 在自上而下的测试过程中,每个单元是通过使用它们来进行测试的,这个过程是由调用这些被测单元的其他独立的单元完成的。首先测试最高层的单元,将所有的调用单元用桩模块替换。接着用实际的调用单元替换桩模块,
21、而继续将较低层次的单元用桩模块替换。重复这个过程直到测试了最底层的单元。自上而下测试法需要测试桩,而不需要测试驱动。图6-7描述了使用测试桩和一些已测试单元来测试单元D的过程,假设单元A,B,C已经用自上而下法进行了测试。6.3.1.自上而下的测试 在自上而下的测试过程中,每个单元是通过使用它们来进行测试的,这个过程是由调用这些被测单元的其他独立的单元完成的。首先测试最高层的单元,将所有的调用单元用桩模块替换。接着用实际的调用单元替换桩模块,而继续将较低层次的单元用桩模块替换。重复这个过程直到测试了最底层的单元。自上而下测试法需要测试桩,而不需要测试驱动。图6-7描述了使用测试桩和一些已测试单
22、元来测试单元D的过程,假设单元A,B,C已经用自上而下法进行了测试。6.3.1.自上而下的测试6.3.1.自上而下的测试由上图得到的是一个基于自上而下组织方法的单元测试计划,其过程可以描述如下:步骤1:测试A单元,使用B,C,D单元的桩模块。步骤2:测试B单元,通过已测试过的A单元来调用它,并且使用C,D单元的桩模块。步骤3:测试C单元,通过已测试过的A单元来调用它,并使用已通过测试的B单元和D单元的桩模块。步骤4:测试D单元,从已测试过的A单元调用它,使用已测试过的B和C单元,并且将E,F和G单元用桩模块代替。6.3.1.自上而下的测试步骤5:测试E单元,通过已测试过的D单元调用它,而D单元
23、是由已通过测试的A单元来调用的,使用已通过测试的B和C单元,并且将F,G,H,I和J单元用桩模块代替。步骤6:测试F单元,通过已测试过的D单元调用它,而D单元是由已通过测试的A单元来调用的,使用已通过测试的B,C和E单元,并且将G,H,I和J单元用桩模块代替。步骤7:测试G单元,通过已测试过的D单元调用它,而D单元是由已通过测试的A单元来调用的,使用已通过测试的B,C,E和F单元有F单元么?E,F,G作为D的出口,应该是有F的,并且将H,I和J单元用桩模块代替6.3.1.自上而下的测试步骤8:测试H单元,通过已测试过的E单元调用它,而E单元是由已通过测试的D单元来调用的,而D单元是由已通过测试
24、的A单元来调用的,使用已通过测试的B,C,E,F和G单元无误?,并且将I,J单元用桩模块代替。步骤9:测试I单元,通过已测试过的E单元调用它,而E单元是由已通过测试的D单元来调用的,而D单元是由已通过测试的A单元来调用的,使用已通过测试的B,C,E,F,G和H单元,并且将J单元用桩模块代替。步骤10:测试J单元,通过已测试过的E单元调用它,而E单元是由已通过测试的D单元来调用的,而D单元是由已通过测试的A单元来调用的,使用已通过测试的B,C,E,F,G,H和I单元同上。这里是指J单元使用其他的单元?。6.3.1.自上而下的测试自上而下单元测试法将单元测试和软件集成策略进行了组合。单元的详细设计
25、是自上而下的,这样的过程使得被测单元按照原设计的顺序进行,因为单元测试的详细设计与软件生命周期代码设计阶段的重叠,所以开发时间将被缩短。在通常的结构化设计中,高等级的单元提供高层的功能,而低等级的单元实现细节,自上而下的单元测试将提供一种早期的“可见”的功能化集成。较低层次的多余功能可以通过自上而下法来鉴别,这是因为没有路径来测试它。6.3.1.自上而下的测试自上而下法是通过桩模块来进行控制的,而且测试用例常常涉及很多的桩模块。对于每个已测单元来说,测试变得越来越复杂,结果是开发和维护的费用也越来越高。依层次进行的自上而下的测试,要达到一个好的覆盖结构也很困难,而这对于一个较为完善、安全的关键
26、性应用来说极为重要,同时这也是很多的标准所要求的。难于达到一个好的覆盖结构也可能导致最终的多余功能和未测试功能之间的混乱。由此,测试一些低层次的功能,特别是错误处理代码,将彻底不切实。6.3.1.自上而下的测试一个自上而下的测试策略成本将高于基于分离的测试策略,这取决于顶层单元和下层单元的复杂程度,以及由于下层单元自身发生变化所带来的显著影响。对于单元测试来说,自上而下的组织方法不是一个好的选择。然而,在各个组成单元已经被单独测试的情况下,用自上而下法进行单元的集成测试是个不错的手段。6.3.2 自下而上的测试自下而上测试时最底层的单元首先被测试,这样就方便了对高层次单元的测试。然后使用前面已
27、经被测试过的被调用单元来测试其他的单元。重复这个过程直到最高层的单元被测试为止。自下而上法需要测试驱动,但是不需要测试桩。图6-8说明了测试D单元时需要的测试驱动和已测单元的情况,假设单元E、F、G、H、I和J已经通过自下而上法进行了测试。6.3.2 自下而上的测试6.3.2 自下而上的测试图6-8显示了一个程序的单元测试的测试计划,该计划使用了基于自下而上的组织方法,其过程如下:步骤1(注意在测试步骤中测试的顺序不是最主要的,本步骤中的所有测试可以同步进行):测试单元H,在调用H单元的E单元处使用一个测试驱动;测试单元I,在调用I单元的E单元处使用一个测试驱动;测试单元J,在调用J单元的E单
28、元处使用一个测试驱动;测试单元F,在调用F单元的D单元处使用一个测试驱动;测试单元G,在调用G单元的D单元处使用一个测试驱动;测试单元B,在调用B单元的A单元处使用一个测试驱动;测试单元C,在调用C单元的A单元处使用一个测试驱动。6.3.2 自下而上的测试步骤2:测试单元E,在调用E单元的D单元处使用一个测试驱动,再加上已测试过的单元H、I和J。步骤3:测试单元D,在调用D单元的A单元处使用一个测试驱动,再加上已测试过的单元E、F、G、H、I和J。步骤4:测试单元A,使用已测试过的单元B、C、D、E、F、G、H、I和J。6.3.2 自下而上的测试自下而上单元测试同样也是真正意义上的单元测试和软
29、件集成策略的结合。因为不需要测试桩,所以所有的测试用例都由测试驱动控制。这样就使得低层次单元附近的单元测试相对简单些(但是,高层次单元的测试可能会变得很复杂)。在使用自下而上法测试时,测试用例的编写可能只需要功能性的设计信息,不需要结构化的设计信息。自下而上单元测试法提供了一种低层次功能性的集成,而较高层次的功能随着单元测试过程的进行按照单元层次关系逐层增加,这就使得自下而上单元测试很容易与测试对象兼容。6.3.2 自下而上的测试但是随着测试逐层推进,自下而上单元测试变得越来越复杂,随之而来的是开发和维护的成本越来越高昂,同样要实现好的结构覆盖也变得越来越困难。低层单元的变化经常影响其上层单元
30、的测试。单元测试的顺序取决于单元的层次关系,较高层次的单元必须要等到较低层次单元通过测试后才能进行测试,所以就形成了“长瘦”型的单元测试阶段。最先被测试的单元是最后被设计的单元,所以单元测试不能与软件生命周期的详细设计阶段重叠。与自上而下测试法一样,自下而上测试法的缺点会随着被测单元之间复杂的联系而放大。6.3.2 自下而上的测试在进行单元测试时,也有固定的模式和方法。在代码编写完成后的单元测试工作主要分为两个步骤:人工静态检查和动态执行跟踪。通常情况下,人工检查需要对单元进行基本的浏览和测试,主要有以下几方面:检查算法的逻辑正确性,模块、其他方法接口的正确性,输入参数的正确性,出错处理,表达
31、式、SQL语句的正确性,以及代码的书写规范。对于单元测试来说主要应该采用白盒测试法对每个模块的内部作跟踪检查测试。6.4 单元测试的过程软件质量的提高需要规范的流程,对软件研发过程进行管理也需要依据规范的过程定义。过程定义包含阶段的划分、阶段的入口/出口准则、阶段的输入/输出、角色和职责、模板和查检表等。将单元测试划分为几个阶段便于对单元测试过程进行控制,体现软件测试可控性。要提高单元测试的质量,首先要制定规范的单元测试过程,研发组、测试组、SCM组、SQA组等能依据单元测试过程定义开展各自的工作,一起确保单元测试的质量。在进行单元测试之前,我们需要对单元测试过程进行定义,如图6-9所示。6.
32、4 单元测试的过程6.4 单元测试的过程下面介绍一下测试过程中各种人员的作用。系统分析设计人员:进行需求跟踪,确保系统需求的实现和更新。进行软件单元可测性分析,确定单元测试的对象、范围和方法。软件开发人员:负责编码和单元测试过程,完成单元测试计划、方案和报告。软件测试人员:参与单元测试计划、方案和报告的评审,对单元测试的计划、设计和执行质量进行监控。根据实际情况,选择参与由开发人员负责的代码检视、单元测试等活动。配置管理人员:对代码及单元测试文档进行配置管理。质量保证(QA)人员:参与编码与单元测试评审,对编码和单元测试过程进行审计。6.4 单元测试的过程单元测试的技术要求:(1)每个被测单元
33、中每条可执行的脚本都被一个测试用例或异常操作所覆盖,即脚本覆盖率达80%。(2)每个被测单元中分支语句取真和取假时,各分支至少执行一次,即分支覆盖率达到80%。(3)每个被测单元中的业务流程和数据流程,必须被一个测试用例、一个异常数据、一次异常操作所覆盖,即异常处理能力达80%。单元测试通过准则:单元功能同设计需求一致,单元接口同设计需求一致,能正确处理输入和异常运行中的错误。单元测试的过程的定义和划分需要考虑企业的实际情况。一般情况下,阶段划分可以分为4个阶段:计划、设计、实现、执行。6.4.1 计划阶段计划阶段应当考虑整个单元测试过程的时间表、工作量、任务的划分情况、人员和资源的安排情况、
34、需要的测试工具和测试方法、单元测试结束的标准及验收的标准等,同时还应当考虑可能存在的风险及针对这些风险的具体处理办法,并输出单元测试计划文件,作为整个单元测试过程的指导。单元测试需要输入的文档有:软件需求规格说明书。软件详细设计说明书。软件编码与单元测试工作任务书。软件集成测试计划。软件集成测试方案。用户文档。6.4.1 计划阶段加强周详设计文件评审对于单元测试过程十分重要。周详设计是单元测试的主要输入,周详设计文件的质量将直接影响到单元测试的质量,所以一定要加强周详设计文件的评审,特别是要写相关测试方案和进行测试用例设计的人员,一定要从写测试用例的角度看这个详细设计是否符合需求,否则后期进行
35、单元测试设计时会发现无法依据周详设计进行单元测试设计。软件组织能将周详设计评审的要点以检查表的形式固化下来,这样在周详设计评审时依据检查表一项项检查,既提高了评审效率,也能确保评审效果。评审流程需要确定如果不满足检查表n%以上的条件,被评审周详设计文件就不能通过,需要重新设计。6.4.1 计划阶段通常周详设计文件有两种形式,一种是流程图的形式,另一种是伪码的形式。用流程图表达的好处是直观,利于单元测试用例设计,缺点是描述性比较差,文件写作麻烦,不利于文件的变更和修改;伪码的方式可能正好相反,文件变更修改简单,能方便地在所有地方增加文字说明,而且翻译成代码更加便捷,但不直观,不利于进行单元测试用
36、例设计。6.4.1 计划阶段周详设计和单元测试设计一定要分离。如果单元测试由测试人员承担,这一点不会有什么问题;如果单元测试由研发人员承担,那么实际操作时能让项目组内做相同或相近任务的成员相互交换,根据对方的详细设计对方的单元测试。这样在单元测试开始之前的周详设计评审阶段就要考虑到后面的分工,安排相关的单元测试设计人员参与相关周详设计的评审。如果代码没有对应的经过评审后的周详设计文件,建议不进行单元测试,而是用代码审查替代单元测试。6.4.2 设计实现阶段设计过程需要具体考虑对哪些单元进行测试,被测单元之间的关系及同其他模块之间单元的关系,具体测试的策略采用哪一种、怎么进行单元测试用例的设计、
37、怎么进行单元测试代码设计、采用何种工具等,并输出单元测试方案文件,用来指导具体的单元测试操作。实现过程需要完成单元测试用例设计、脚本、测试驱动模块、测试桩模块的编写工作,输出单元测试用例文件、相关测试代码。单元测试中的技术解决过程描述如图6-10所示。6.4.2 设计实现阶段6.4.2 设计实现阶段6.4.2 设计实现阶段编程过程中,开发人员根据“编程计划”编写软件的代码,并随时记录编程技术、问题与对策、心得体会等,产生编程文档(类似于编程日记)。开发人员在编写完成每个模块时,必须对自己的代码进行必要的审查和测试。代码审查过程是由品质保证部对代码按照相关代码规范进行审查,并填写代码审查报告。单
38、元测试过程由开发人员首先撰写单元测试用例。开发人员根据单元测试计划和相应的测试用例来测试同伴的代码,产生测试报告。6.4.3 执行评估阶段执行阶段的主要工作是搭建单元测试环境、执行测试脚本、记录测试结果,如果发现错误,研发人员需要负责错误的修改,同时进行回归测试,该阶段结束需要提交单元测试报告。为了确保单元测试工作产品的准确性,需要对测试代码和脚本进行走读或检视,对测试文件进行评审。这些工作产品应该纳入到设置管理,对于其修改要走设置变更流程,并及时发布其设置状态,这样能保持单元测试工作产品的一致性和可回溯性。企业必须制订覆盖率指标和质量目标来指导和验收单元测试。6.4.3 执行评估阶段单元测试
39、的退出条件:被测代码语句覆盖率满足单元测试计划中制定的代码覆盖率要求。测试用例执行覆盖率应达100%。单元测试分析报告通过评审。6.4.3 执行评估阶段单元测试必须制订一定的覆盖率指标和质量目标,来指导单元测试设计和执行,同时作为单元测试验收的标准。设计用例时,可针对要达到的覆盖率指标来设计用例,而在测试执行时,能依据覆盖率分析工具分析测试是否达到了覆盖率指标,如果没达到,需要分析哪些部分没有覆盖到,从而补充用例来达到覆盖率指标。而单元测试质量目标的制定,需要符合软件企业的实际过程能力,这依赖于软件企业以前单元测试过程度量数据的积累,不能凭空制造出来。有了以前度量数据的积累,完全能了解当前组织
40、的单元测试能力,例如单元测试每千行代码发现的缺陷数是多少。如果单元测试统计结果没有落到这个质量目标范围内,说明单元测试过程中某些方面存在一些问题,需要对过程进行审计后找出问题原因进行改进。6.4.3 执行评估阶段执行阶段的主要工作是搭建单元测试环境、执行测试脚本、记录测试结果,如果发现错误,研发人员需要负责错误的修改,同时进行回归测试,该阶段结束需要提交单元测试报告。为了确保单元测试工作产品的准确性,需要对测试代码和脚本进行走读或检视,对测试文件进行评审。这些工作产品应该纳入到设置管理,对于其修改要走设置变更流程,并及时发布其设置状态,这样能保持单元测试工作产品的一致性和可回溯性。企业必须制订
41、覆盖率指标和质量目标来指导和验收单元测试。6.4.3 执行评估阶段这些指标确定下来后,一定要严格推行。单元测试需要输出的文档有:单元测试计划。单元测试方案。需求跟踪说明书或需求跟踪记录。代码静态检查记录。正规检视报告。问题记录。问题跟踪和解决记录。软件代码开发版本。单元测试报告。软件编码与单元测试任务总结报告。6.6 本章小结单元测试是对软件中最小的可测试单元进行检查和验证,是软件开发中要进行的最低级别的测试活动,也称为模块测试。单元测试的目的除了要发现编码中引入的错误和发现代码和周详设计不一致的地方之外,还有一个目的是为了确保周详设计的质量。单元测试的内容有:模块接口测试、局部数据结构测试、路径测试、错误处理测试、边界测试等。驱动模块相当于被测模块的主程序,它接收测试数据,把这些数据传送给被测模块,启用被测模块,最后输出实测结果。主模块作为驱动模块,与之直接相连的模块用桩模块代替。桩模块是集成测试前要为被测模块编制一些模拟其下级模块功能的“替身”模块,以代替被测模块的接口,接受或传递被测模块的数据,这些专供测试用的“假”模块称为被测模块的桩模块。单元测试的两种常用策略,第一种是准备、执行、断言,第二种是构造函数测试。单元测试的过程可以分为4个阶段:计划、设计、实现、执行。