测试(Testing)这个词很容易理解,那么什么是单元(Unit)呢? 一个单元指的是应用程序中可测试的最小的一组源代码。一组源代码可测试,一般要求其有明确的输入和输出。因此,一般来讲,源代码中包含明确的输入和输出的 每一个方法被认为是一个可测试的单元。注意,这里指的输出,并不局限于方法的返回值或对输入参数的改变,而包括了方法的执行过程中,改变的任何数据。
单元在程序里可以简单的理解为一个模块,一个方法。单元测试也就是在完成每个模块后都进行的测试。从确保每个模块没有问题,从而提高整体的程序质量。
单元测试的目的,是将应用程序的所有源代码,隔离成最小的可测试的单元,保证每个单元的正确性。理想情况下,如果每个单元都能保证正确,就能保证应用程序整体相当程度的正确性。
另一方面,单元测试也是一种特殊类型的文档,相对于书面的文档,测试脚本本身往往就是对被测试代码的实际的使用代码,对于帮助开发人员理解被测试单元的使用是相当有帮助的。
测试分为黑盒测试白盒测试。
黑盒测试:只知道WHAT下进行的测试,黑盒测试一般由测试人员进行。
单元测试是一种白盒测试,目的是验证所设计的类是否符合需求。
在设计测试用例时,应当包括合理的输入条件和不合理的输入条件。
白盒测试:知道被测软件如何(HOW)完成功能和完成什么样的功能(WHAT),白盒测试一般由开发人员完成。
1)隔离
要进行单元测试,首先就是要将被测试的单元,与所有外部依赖进行隔离。一般来讲,进行隔离主要有两种可选的技术,一种叫Stub,一种叫Mock。对于两个的区别和取舍,Martin Fowler有一个很经典的文章:Mocks Aren't Stubs。简单来说,如果要Stub或Mock一个方法,Stub指的是,为了测试,手动写一个相同签名的方法,根据我的测试需要,对给定的输入参数,返回一定的输出;而Mock则借助一定的框架组件,自动生成一个方法的实现,对于给定的输入,返回一定的输出。当然,所谓的Stub或Mock,并不单指对方法,尤其是在面向对象编程中,也可以是对属性,对对象或者对接口,等等。
为了方便实现隔离,对软件设计和开发的一个潜在的激励是,软件模块的设计人员和开发人员,不得不时时思考,在当前语言支持的各种特性的条件下,如何使得所写的代码,能够被方便的被隔离。虽然这看起来很像是一种限制,但是和软件设计的SOLID原则,其实是不谋而合的,因此,也就未尝不是一个优点了。
模块接口测试:对通过被测模块的数据流进行测试。为此,对模块接口,包括参数表、调用子模块的参数、全程数据、文件输入/输出操作都必须检查。
局部数据结构测试:设计测试用例检查数据类型说明、初始化、缺省值等方面的问题,还要查清全程数据对模块的影响。
路径测试:选择适当的测试用例,对模块中重要的执行路径进行测试。对基本执行路径和循环进行测试可以发现大量路径错误。
错误处理测试:检查模块的错误处理功能是否包含有错误或缺陷。例如,是否拒绝不合理的输入;出错的描述是否难以理解、是否对错误定位有误、是否出错原因报告有误、是否对错误条件的处理不正确;在对错误处理之前错误条件是否已经引起系统的干预等。
边界测试:要特别注意数据流、控制流中刚好等于、大于或小于确定的比较值时出错的可能性。对这些地方要仔细地选择测试用例,认真加以测试。
此外,如果对模块运行时间有要求的话,还要专门进行关键路径测试,以确定最坏情况下和平均意义下影响模块运行时间的因素。这类信息对进行性能评价是十分有用的。
6.单元测试的步骤
通常单元测试在编码阶段进行。在源程序代码编制完成,经过评审和验证,确认没有语法错误之后,就开始进行单元测试的测试用例设计。利用设计文档,设计可以验证程序功能、找出程序错误的多个测试用例。对于每一组输入,应有预期的正确结果。
模块并不是一个独立的程序,在考虑测试模块时,同时要考虑它和外界的联系,用一些辅助模块去模拟与被测模块相联系的其它模块。这些辅助模块分为两种:
驱动模块:相当于被测模块的主程序。它接收测试数据,把这些数据传送给被测模块,最后输出实测结果。
桩模块:用以代替被测模块调用的子模块。桩模块可以做少量的数据操作,不需要把子模块所有功能都带进来,但不允许什么事情也不做。
如果一个模块要完成多种功能,且以程序包或对象类的形式出现,例如Ada中的包,Modula中的模块,C++中的类。这时可以将这个模块看成由几个小程序组成。对其中的每个小程序先进行单元测试要做的工作,对关键模块还要做性能测试。对支持某些标准规程的程序,更要着手进行互联测试。有人把这种情况特别称为模块测试,以区别单元测试。
单元测试是软件测试的基础,因此单元测试的效果会直接影响到软件的后期测试,最终在很大程度上影响到产品的质量。从如下几个方面就可以看出单元测试的重要性在何处。
时间方面:如 果认真的做好了单元测试,在系统集成联调时非常顺利,因此会节约很多时间,反之那些由于因为时间原因不做单元测试或随便做做的则在集成时总会遇到那些本应 该在单元测试就能发现的问题,而这种问题在集成时遇到往往很难让开发人员预料到,最后在苦苦寻觅中才发现这是个很低级的错误而在悔恨自己时已经浪费了很多 时间,这种时间上的浪费一点都不值得,正所谓得不偿失。
测试效果:根 据以往的测试经验来看,单元测试的效果是非常明显的,首先它是测试阶段的基础,做好了单元测试,在做后期的集成测试和系统测试时就很顺利。其次在单元测试 过程中能发现一些很深层次的问题,同时还会发现一些很容易发现而在集成测试和系统测试很难发现的问题。再次单元测试关注的范围也特殊,它不仅仅是证明这些 代码做了什么,最重要的是代码是如何做的,是否做了它该做的事情而没有做不该做的事情。
测试成本:在单元测试时某些问题就很容易发现,如果在后期的测试中发现问题所花的成本将成倍数上升。比如在单元测试时发现1个问题需要1个小时,则在集成测试时发现该问题需要2个小时,在系统测试时发现则需要3个小时,同理还有定位问题和解决问题的费用也是成倍数上升的,这就是我们要尽可能早的排除尽可能多的bug来减少后期成本的因素之一。
产品质量:单元测试的好与坏直接影响到产品的质量,可能就是由于代码中的某一个小错误就导致了整个产品的质量降低一个指标,或者导致更严重的后果,如果我们做好了单元测试这种情况是可以完全避免的。
综上所述,单元测试是构筑产品质量的基石,我们不要因为节约单元测试的时间不做单元测试或随便做而让我们在后期浪费太多的不值得的时间,我们也不愿意因为由于节约那些时间导致开发出来的整个产品失败或重来!
1)有测试总比没测试好
2)测试尽可能的小而快
3)使测试自动化
4)进行代码覆盖检测
5)在写任何新的实现代码或测试代码之前,先修复所有运行失败的测试
6)再简单的代码也需要测试
7)特别注意输入参数的边界案例
8)不要只测试正常流程,还要测试可选和例外流程
9)对于每一个报告的Bug,写一个对应的测试用例,方便回归测试
10)时刻注意,所有测试都通过,不代表程序就真的100%没问题,测试永远只是辅助