单元测试总结

 A、单元测试原理概括程序员

优良的单元测试具备如下的特色:简称为 A-TRIP。web


自动性(Automatic)
spring

完备性(Thorough)数据库

可重复性(Repeatable)数组

独立性(Independent)安全

专业性(Professional)
下面让咱们逐一理解它们的含义。服务器

自动性
单元测试是自动执行的,这里的自动指两个方面:1执行测试,2检查测试结果 
执行测试应该是足够简单的,这样,咱们就能够随时随地的进行测试。所以,执行相应测试就应该像在IDE中点击一个按钮,或者在命令行中打一个命令那么简单。一些IDE甚至会在后台连续的进行测试。 
维护这个环境很关键,不要引入哪些会破坏自动测试模型,须要手工进行干预的单元测试。若是测试须要一些环境(网络,数据库,等等),那就把它作为测试的一部分。Mock对象能够有效的隔离对外部的依赖。 
运行测试的不光你一我的,还应该有一台机器对全部提交的代码持续的运行全部的测试。这种自动、无人职守的检查的做用就像一个定位杆,这种安全机制保证全部提交的东西不会破坏任何测试。理想状况下并没必要须这样子,由于你能够依靠每个开发人员都会自行运行全部必需的测试。回到现实,可能某个家伙再某个遥远的地方并无执行必需的测试。也许在他的机器上有一些代码可以保证一切没问题,可他们却没有提交代码。这样虽然在本地能够执行,其余地方就会出问题。 
最后,自动性的含义还有测试必须可以自行判断成功仍是失败。让一我的(你或者其余倒霉蛋)经过检查测试产生的数据结果来判断代码的正确性,这是让项目失败不错的方式。测试的自检查是一致回归的重要特性。人类并不擅长这种重复性的工做,另外,在项目里,咱们还有不少更重要的事情去作。 
测试的自动执行和自动检查是很是关键的,这意味着你不用花太多的心思再这上面,它已经成为了项目的一部分。测试是项目安全保护网的重要组成部分。它在你掉下来时接住你,且不会挡道,这样你就能够集中精力走"钢丝"了。网络

完全性
良好的测试应该是完全的,全部可能会出错的地方都应该测到。如何可以作到这一点呢?一种极端状况,测试每一行代码、每个分支、每个抛出的异常,诸如此类;另外一个极端,只测试最可能的状况:边界条件、数据丢失、数据格式无效,等等。这就须要根据项目状况进行区分了。 
若是追求较高的测试覆盖度,那就须要寻求代码覆盖工具来帮忙了(例如:免费的nounit,quilt,商用的Clover),用这些工具能够知道到底有多少代码是被测试覆盖的。 
有一个事实须要注意,Bug在代码中的分布状况是不均匀的,而是喜欢汇集在有问题的地方(不少昆虫都这样,例如:苍蝇)。 
这种现象引出了一段很是著名的呼声“别打补丁,重写”。一般,从头重写一段有一堆问题的代码的代价和痛苦程度要低得多。固然,有了单元测试,从头重写代码也安全多了,单元测试能够保证新代码可以按照预约的执行。app


可重复性
测试用例之间是独立的同时,也应该是独立于环境的。目标就是保持每一个测试可以以任意顺序、不断的重复执行,且获得一样的结果。这意味着测试不能依赖于不可控的任何外部环境。 
使用Mock对象来隔离测试,保证测试不依赖于外部环境。若是必须依赖一些条件(例如:数据库),那就要保证这个条件不受其余开发人员的干扰。每一个开发人员都应该有本身的sandbox,不一样的数据库实例啦,web服务器啦。 
没有可重复性保证,可能会在最糟糕的时侯遇到一些让你奇怪的问题,更糟糕的是,这些奇怪的问题一般都是虚幻的,并非一个真正的bug,只是测试自身的问题。真不值得为这些虚幻的问题浪费时间。 
每一个测试用例,每次执行都应该获得一样的结果。若是测试结果是失败,那就说明是在代码里面存在bug,而不是有其余问题引发的错误。框架


独立性
测试代码必须保持精简、整洁,这就要求测试是专一的、与环境和其余测试隔离的(要记住,别的开发人员可能同时在运行这些测试)。 
在书写测试时,保证每一个测试只作一件事情。这不是说在一个测试方法里只写一个assert,而是说在一个测试方法里应该只专一于一个、或者几个共同提供某些功能的方法产品代码。有些时候一个测试方法只可以测试了一个复杂的产品代码方法的一部分,就须要一套测试方法来完整的测试产品代码的方法了。 
理想状况下,咱们但愿在测试代码和潜在的bug之间创建起关联。也就是说,当一个测试方法fail的时候,可以定位到对应代码中的bug。独立性也意味着test之间没有相互依赖。每个test都是能够独立运行的,而不须要必须在其余test以后才能运行。每个test应该是一个孤岛。

专业性
为单元测试所写的代码是货真价实的,甚至有些人会争辩说,比提交个客户的源代码还要货真价实。这意味着,测试代码的书写和维护应该保持和生产代码同样的专业水准。产品代码中必须遵循的经常使用的设计准则,如:数据封装、DRY原则、高内聚、低耦合等等,在测试代码中也同样要遵照。 
测试代码很容易就会写成linear的样式,代码里面充次着一样的内容,不断的重复一样的代码,却鲜见方法和对象。这样很糟糕,测试代码必须和生产代码按同等对待来书写。这就要把哪些经常使用的、重复部分的代码抽取出来成为一个方法,这样就能够在多处调用。 
这样就会慢慢积累出一些测试的方法,能够封装在一个类里面了。别争论什么,就算这个类只是用来测试的,就建立一个好了。这样作不进没问题,并且是应该获得鼓励的:测试代码是货真价实的代码。某些状况下,咱们也许须要建立一个更大的框架,或者建立一个数据驱动的测试工具。 
不要浪费时间测试那些没必要要的方面,咱们不是为了测试而建立test。测试的完整性要求测试方法的每个可能产生bug的方面。若是没有产生bug的可能,就不要作测试。好比get set方法,就不必作测试了。但若是这些get set方法中包含了业务逻辑,哪就有必要进行测试了。 
最后,测试代码应该和生产代码是同一规模量级的。是的,你绝对没有看错。若是产品代码有20000行,那么测试代码至少也应该是20000行,甚至更多。测试代码的量也很大,就必须保持整洁、精简,有良好的设计,结构良好的,同生产代码同样的专业。 
[摘自]http://www.iteye.com/topic/30932


--------------------------------------------------------------------------------

B、单元测试目的
(1)单元测试目的:
首先保证代码质量。
其次保证代码的可维护。
再此保证代码的可扩展。

(2)单元测试的优势
一、它是一种验证行为。
    程序中的每一项功能都是测试来验证它的正确性。它为之后的开发提供支缓。就算是开发后期,咱们也能够轻松的增长功能或更改程序结构,而不用担忧这个过程当中会破坏重要的东西。并且它为代码的重构提供了保障。这样,咱们就能够更自由的对程序进行改进。
二、它是一种设计行为。
    编写单元测试将使咱们从调用者观察、思考。特别是先写测试(test-first),迫使咱们把程序设计成易于调用和可测试的,即迫使咱们解除软件中的耦合。
三、它是一种编写文档的行为。
    单元测试是一种无价的文档,它是展现函数或类如何使用的最佳文档。这份文档是可编译、可运行的,而且它保持最新,永远与代码同步。
四、它具备回归性。
    自动化的单元测试避免了代码出现回归,编写完成以后,能够随时随地的快速运行测试。

(3)单元测试的范畴
    若是要给单元测试定义一个明确的范畴,指出哪些功能是属于单元测试,这彷佛很难。但下面讨论的四个问题,基本上能够说明单元测试的范畴,单元测试所要作的工做。
一、 它的行为和我指望的一致吗?
    这是单元测试最根本的目的,咱们就是用单元测试的代码来证实它所作的就是咱们所指望的。
二、 它的行为一直和我指望的一致吗?
    编写单元测试,若是只测试代码的一条正确路径,让它正确走一遍,并不算是真正的完成。软件开发是一个项复杂的工程,在测试某段代码的行为是否和你的指望一 致时,你须要确认:在任何状况下,这段代码是否都和你的指望一致;譬如参数很可疑、硬盘没有剩余空间、缓冲区溢出、网络掉线的时候。
三、 我能够依赖单元测试吗?
    不能依赖的代码是没有多大用处的。既然单元测试是用来保证代码的正确性,那么单元测试也必定要值得依赖。
四、 单元测试说明个人意图了吗?
    单元测试可以帮咱们充分了解代码的用法,从效果上而言,单元测试就像是能执行的文档,说明了在你用各类条件调用代码时,你所能指望这段代码完成的功能。

[摘自]http://www.iteye.com/topic/38205
--------------------------------------------------------------------------------
我想说的就是,在整个软件行业来讲,无论是程序员也好,老板也好,客户也好,都漠视了一个基本的事实:单元测试代码是软件产品的一个必须组成部分!不提供测试代码的软件产品就是偷工减料,以次充好的奸商行为!

[摘自]http://www.iteye.com/topic/14021
--------------------------------------------------------------------------------

C、基于mock对象和JUnit框架简化Spring Web组件单元测试

对于Java组件开发者来讲,他们都盼望拥有一组可以对组件开发提供全面测试功能的好用的单元测试。一直以来,与测试独立的Java对象相比,测试传统型J2EE Web组件是一项更为困难的任务,由于Web组件必须运行在某种服务器平台上而且它们还要与基于HTTP的Web交互细节相联系。

易测性(在框架中测试每一个组件而无论其具体种类)是Spring框架所提倡的关键原则之一。从这一角度看,Spring是对核心J2EE模型的一个重大改进—在之前状况下,在容器外进行组件测试是很难实现的,并且即便是容器内测试也每每要求复杂的安装过程。

本文正是想集中探讨Spring的易测性特征—它能使得对Web组件进行单元测试就象测试普通Java对象(POJO)同样容易。

1、Spring Mock类简介

Mock对象是一个术语,原来主要流行于eXtreme程序员和JUnit小组中。在单元测试上下文中,一个mock对象是指这样的一个对象——它可以用一些“虚构的占位符”功能来“模拟”实现一些对象接口。在测试过程当中,这些虚构的占位符对象可用简单方式来模仿对于一个组件的指望的行为和结果,从而让你专一于组件自己的完全测试而不用担忧其它依赖性问题。

Spring从J2EE的Web端为每一个关键接口提供了一个mock实现:

MockHttpServletRequest—几乎每一个单元测试中都要使用这个类,它是J2EE Web应用程序最经常使用的接口HttpServletRequest的mock实现。

MockHttpServletResponse—此对象用于HttpServletResponse接口的mock实现。

MockHttpSession—这是另一个常用的mock对象(后文将讨论此类在会话绑定处理中的应用)。

DelegatingServletInputStream—这个对象用于ServletInputStream接口的mock实现。

DelegatingServletOutputStream—这个对象将代理ServletOutputStream实现。在须要拦截和分析写向一个输出流的内容时,你可使用它。

总之,在实现你本身的测试控制器时,上面这些对象是最为有用的。然而,Spring也提供了下列相应于其它不太经常使用的组件的mock实现(若是你是一个底层API开发者,那么你可能会找到其各自的相应用法):

MockExpressionEvaluator—这个mock对象主要应用于你想开发并测试你本身的基于JSTL的标签库时。

MockFilterConfig—这是FilterConfig接口的一个mock实现。

MockPageContext—这是JSP PageContext接口的一个mock实现。你会发现这个对象的使用有利于测试预编译的JSP。

MockRequestDispatcher—RequestDispatcher接口的一个mock实现,你主要在其它mock对象内使用它。

MockServletConfig—这是ServletConfig接口的一个mock实现。在单元测试某种Web组件(例如Struts框架所提供的Web组件)时,要求你设置由MockServletContext所实现的ServletConfig和ServletContext接口。

那么,咱们该如何使用这些mock对象呢?咱们知道,HttpServletRequest是一个持有描述HTTP参数的固定值的组件,而正是这些参数驱动Web组件的功能。MockHttpServletRequest,做为HttpServletRequest接口的一个实现,容许你设置这些不可改变的参数。在典型的Web组件测试情形下,你能够实例化这个对象并按以下方式设置其中的任何参数:
//指定表单方法和表单行为

 

MockHttpServletRequest request = new MockHttpServletRequest("GET", "/main.app");
request.addParameter("choice", expanded);
request.addParameter("contextMenu", "left");

 

一样地,你能够实例化并全面地控制和分析HttpResponse和HttpSession对象。接下来,让咱们简要观察Spring所提供的特定的JUnit框架扩展。

2、JUnit框架扩展

Spring提供了下列一些特定的JUnit框架扩展:

AbstractDependencyInjectionSpringContextTests—这是一个针对全部测试的超类,其具体使用依赖于Spring上下文。

AbstractSpringContextTests—这是一个针对全部的JUnit测试情形的超类。它使用一个Spring上下文。而且,通常在测试中不是直接使用它,而是使用AbstractDependencyInjectionSpringContextTests或者AbstractTransactionalSpringContextTests这样的派生类。

AbstractTransactionalSpringContextTests—这是一个针对全部测试的超类,咱们通常把它应用在事务相关的测试中。注意,一旦完成每一个测试它就会正常地回滚事务;并且你须要重载onSetUpInTransaction和onTearDownInTransaction方法以便手工开始并提交事务。

AbstractTransactionalDataSourceSpringContextTests—这是AbstractTransactionalSpringContextTests的一个子类,它使用了Spring的基于JDBC的jdbcTemplate工具类。
全部上面这些扩展将极大程度地简化在测试时对于相关操做的依赖性注入和事务管理。

5、事务性单元测试

到目前为止,你已看到了相对简单的JUnit测试—它仅发生在用mock对象支持的一个控制器的上下文中。可是,若是测试一个Web组件只有在一个事务性上下文(例如,经过依赖性注入与Hibernate集成到一块儿)中才有意义的状况又会怎么样呢?没必要担忧,Spring MVC为JUnit框架提供了一个体面的扩展集合—它能准确地提供依赖性注入和事务安全测试(也就是,任何更新在测试完成后都将被回滚)。

测试步骤:

让咱们看一种假想的情形—你要实现一个组件(例如MyTransactionalController)测试,该组件运行在一个事务性的上下文中(也即,其方法调用的结果发生在一个事务内而且它应该在测试运行完后被回滚):

1.建立一个定制的JUnit类(MyTransactionalControllerTest),它扩展了Spring的JUnit扩展类

AbstractTransactionalSpringContextTests:

import org.springframework.test.AbstractDependencyInjectionSpringContextTests;

public class MyTransactualControllerTest extends

AbstractTransactionalSpringContextTests {

public class.

 

2.为了实现从Spring内置的单元测试中发现Spring管理的bean,你须要重载getConfigLocations()方法而且返回上下文文件位置的String数组,请看以下:

 

protected abstract String[] getConfigLocations(){

return new String[] {"classpath:/test/spring-context.xml"};

}

 

3.拥有该类的一个测试属性及其相关联的getter和setter。因为AbstractTransactionalSpringContextTests利用了auto-wiring(这是Spring框架的一个特性—可以根据类属性的名字识别类依赖性而且用Spring bean填入相匹配的名字或ID)技术并且在测试时它将自动地解决类的依赖性问题,因此在Spring上下文文件中该类属性具备与Spring管理的bean同样的名字而且在测试时每一个属性都有一个适当命名的setter:

 

public MyTransactualController myTransactualController;

/**

* @返回myTransactualController。

*/

public MyTransactualController getMyTransactualController() {

return this.myTransactualController;

}

/**

*@参数myTransactualController。

*/

public void setMyTransactualController(

MyTransactualController myTransactualController) {

this.myTransactualController = myTransactualController;

}

 

4.就象你一般操做“普通的”JUnit测试同样实现测试方法:

 

public void testCorrectBehavior() throws Exception{

//运行该事务性方法

myTransactualController.submitPayment( new Payment( 100 ) );

assertTrue( myTransactualController.isValid() );

}

 

注意,你是在调用可能会更新数据库的方法submitPayment。Spring的JUnit扩展(AbstractTransactionalSpringContextTests)将在这个测试方法结束后实现自动回滚。

5.若是你须要执行任何安装或清除任务,则能够重载AbstractTransactionalSpringContextTests的onSetUpBeforeTransaction()或onSetUpInTransaction()方法。AbstractTransactionalSpringContextTests将重载从TestCase继承来的setUp()和tearDown()方法而且使其成为final类型。

相关文章
相关标签/搜索