`

基于mock对象和JUnit框架简化Spring Web组件单元测试(1)

    博客分类:
  • java
 
阅读更多


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

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

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

一、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框架扩展。

二、JUnit框架扩展

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

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

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

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

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

三、普通Web测试情形

在此,我们将回顾测试Web组件的普通情形以及怎样在其中使用Spring的mock对象和JUnit框架扩展。

(一)确定一个正确的视图

基于输入参数生成正确的视图可能是在操作一个Web应用程序时最普通的功能。在Spring MVC的上下文中,这意味着Spring MVC将基于参数的状态返回某种ModelAndView对象。你可以通过简单地利用如下的Mock对象以一个常规JUnit测试方式来测试这项功能:

public void final testGettingToDetails throws Exception{
MyController myController = new MyController();
myController.setDetailsView( detailsViewName );
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setMethod("POST");
request.addParameter("viewDetails", "true");
ModelAndView modelAndView = myController.handleRequest(request, response);
assertEquals("Incorrect view name", detailsViewName,modelAndView.getViewName());

 既然控制器很可能会利用一些服务对象来决定结果视图,那么你还可以定制控制器中所用的这些mock服务对象。关于利用定制对象的更多资料,请参考mockobjects.com。

(二)会话相关的操作

 

对于任何J2EE Web应用程序来说,另一个必须实现的操作是HttpSession绑定处理。例如,Spring MVC可能需要决定是否一个对象处于会话中及其具体状态以便产生正确的结果。你可以利用MockHttpSession对象和JUnit框架测试这种情形。请参考如下的代码片断:

public void testInvokesCorrectMethodWithSession() throws Exception {

TestController cont = new TestController();

MockHttpServletRequest request = new MockHttpServletRequest(

"GET", "/invoiceView.app");

request.setSession(new MockHttpSession(null));

HttpServletResponse response = new MockHttpServletResponse();

ModelAndView mv = cont.handleRequest(request, response);

assertTrue("Invoked loggedIn method", cont.wasInvoked("loggedIn"));

assertTrue("view name is ",mv.getViewName().equals("loggedIn"));

assertTrue("Only one method invoked", cont.getInvokedMethods() == 1);

//测试控制器但是不使用会话

request = new MockHttpServletRequest("GET", "/invoiceView.app");

response = new MockHttpServletResponse();

try {

cont.handleRequest(request, response);

fail("Should have rejected request without session");

}

catch (ServletException ex) {

//在此加入期盼的异常处理

}

}

 

(三)转发和重定向

一个Spring MVC组件执行的操作能够导致转发或重定向到另一个URL。如果你的目标是分析转发或重定向的结果,那么你可以测试这一情形—通过分析MockHttpResponse对象并进而确定有哪些内容包含在它的重定向或转发值中,如下所示:

String responseString = ((MockHttpServletResponse)httpResponse).getForwardedUrl();

assertEquals( "Did not forward to the expected URL", responseString, expectedString);

 

四、生成正确的二进制输出

如何确定你有多少次必须实现“View as PDF”这一功能?下面的JUnit代码片断使用mock输出流对象实现这一功能的正确测试:

public void testPDFGeneration() throws Exception{

MockHttpServletRequest request = new MockHttpServletRequest();

MockHttpServletResponse response = new MockHttpServletResponse();

viewInvoiceAsPDFController.handleRequest( request, response ); 

byte[] responsePDFValues = response.getContentAsByteArray();

byte[] expectedPDFValues = loadBytesFromTestFile();

assertTrue( "Did not generate expected PDF content.", 

Arrays.equals(responsePDFValues,expectedPDFValues ));

}

 

注意,在此你的控制器ViewInvoiceAsPDFController不是返回ModelAndView对象,而是产生了二进制输出—你可以使用一个二进制的数组形式来捕获此控制器并对此进行正确性评价。

五、事务性单元测试

到目前为止,你已看到了相对简单的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类型。

六、小结

至此,你已经学习了如何使用Spring单元测试框架和Web组件mock对象。通过使用这两个工具,你将会极大地提高你的Web组件的开发效率。

 

 

 

 

分享到:
评论

相关推荐

    java单元测试 spring mock的使用

    在java web应用中,通过mock技术可以模拟spring容器,从而进行单元测试,详细可以参见本文

    struts-junit spring-mock spring-test junit的javadoc.jar文档

    struts-junit spring-mock spring-test junit等的javadoc.jar格式的API文档,直接导入Eclipse/MyEclipse/Netbeans等IDE即可实现快速API查询。 包含以下文件: File name -------------------------------------- ...

    spring-mock.jar

    org.springframework.mock.web.MockHttpServletRequest.class org.springframework.mock.web.MockHttpServletResponse.class org.springframework.mock.web.MockHttpSession.class org.springframework.mock.web....

    单元测试JUnit常用断言方法

    单元测试 JUnit 断言 注解 mock Unitils spring 注解

    Junit + Hamcrest + Mockito 单元测试 Jar包

    包含 Junit + Hamcrest + Mockito 单元测试涉及到的三个jar包,分别是junit.jar、hamcrest-2.2.jar、mockito-core-3.2.4.jar

    基于Springboot+Junit+Mockito做单元测试的示例

    本篇文章主要介绍了基于Springboot+Junit+Mockito做单元测试的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    junit单元测试及Mock应用,超详细的PPT实战应用

    本文档ppt讲述了软件测试及单元测试的概念及区别、Junit框架概念及基本应用,并有详细的代码示例、异常测试的概念的测试代码、 超时测试的概念及测试代码、忽略测试的概念及测试代码、Mock学习及前后端代码调试、...

    mock junit4

    mock junit4

    单元测试高级特性-mock模拟网络请求

    单元测试高级特性--mock模拟网络请求单元测试高级特性--mock模拟网络请求单元测试高级特性--mock模拟网络请求单元测试高级特性--mock模拟网络请求单元测试高级特性--mock模拟网络请求单元测试高级特性--mock模拟网络...

    mockito单元测试使用

    虽然测试分为单元测试,集成测试,系统测试等等,但是作为开发,我们可能不需要做这么多的测试(有时甚至不做……)接下来就说说和开发息息相关的单元测试以及集成测试。 单元测试就是模块测试,我的理解一个模块...

    单元测试junit-4.13.1.zip

    Junit单元测试相关jar工具。包含:hamcrest-core-1.3和junit-4.13.1 单元测试 (英语: Unit Testing )又称为 模块测试 ,是针对 程序模块 ( 软件设计 的最小单位)来进行正确性检验的测试工作。. 程序单元是应用...

    junit mock

    junit mock 测试用的啊 csdn 垃圾

    Java单元测试(Junit+Mock+代码覆盖率)

    JUnit是Java单元测试框架,已经在Eclipse中默认安装。目前主流的有JUnit3和JUnit4。JUnit3中,测试用例需要继承TestCase类。JUnit4中,测试用例无需继承TestCase类,只需要使用@Test等注解。先看一个Junit3的样例...

    Junit+EasyMock单元测试的jar包

    Junit+EasyMock单元测试的jar包,由于一次上传的资料大小首先,所以只上传了部分资料.

    Junit实战第二版

    包括探索JUnit的核心、软件测试原则、测试覆盖率与开发、使用stub进行粗粒度测试、使用mock objects进行测试、容器内测试、从Ant中运行JUnit测试、从Maven2中运行JUnit测试、持续集成工具、表示层的测试、Ajax测试、...

    spring-mock-2.0-rc3.jar

    Spring针对J2EE的常用Web接口提供了Mock,这些组件被发布于spring-mock.jar,介绍如下: MockHttpServletRequest:HttpServletRequest接口的mock实现。 MockHttpServletResponse:HttpServletResponse接口的mock...

    Junit测试 简单用例

    此实例是Junit测试的例子,有单元测试的的例子 ,例如testCase 也有综合测试的例子 例如ALLTest 里面还有一些给读者的建议

    mockito源码 供java同学学习,用于在自动化单元测试中创建测试对象,为TDD或BDD提供支持

    其主要功能是创建和配置Mock对象,以简化存在外部依赖的类的测试。通过使用Mockito,我们可以虚拟出一个外部依赖,这样可以降低测试组件之间的耦合度,使得我们能更专注于代码的流程和结果,从而更好地实现测试目的...

    Junit实战第二版 中文完整版

    包括探索JUnit的核心、软件测试原则、测试覆盖率与开发、使用stub进行粗粒度测试、使用mockobjects进行测试、容器内测试、从Ant中运行JUnit测试、从Maven2中运行JUnit测试、持续集成工具、表示层的测试、Ajax测试、...

Global site tag (gtag.js) - Google Analytics