文章节选自:
http://lanmh.iteye.com/blog/207500
http://static.springsource.org/spring/docs/2.5.x/reference/testing.html
Spring 的 mock 组建主要分3部分:
1. JNDI
The org.springframework.mock.jndi package contains an implementation of the JNDI SPI, which is useful for setting up a simple JNDI environment for test suites or stand-alone applications. If, for example, JDBC DataSources get bound to the same JNDI names in test code as within a J2EE container, both application code and configuration can be reused in testing scenarios without modification.
2. Servlet API
The org.springframework.mock.web package contains a comprehensive set of Servlet API mock objects, targeted at usage with Spring's Web MVC framework, which are useful for testing web contexts and controllers. These mock objects are generally more convenient to use than dynamic mock objects (e.g., EasyMock) or existing Servlet API mock objects (e.g., MockObjects).
3. Portlet API
The org.springframework.mock.web.portlet package contains a set of Portlet API mock objects, targeted at usage with Spring's Portlet MVC framework.
项目中主要用到的是2. Servelt API(org.springframework.mock.web),可以方便的mock出httprequest,httpsession等对象。
Spring Mock类简介
Mock对象是一个术语,原来主要流行于eXtreme程序员和JUnit小组中。在单元测 试上下文中,一个mock对象是指这样的一个对象——它能够用一些“虚构的占位符”功能来“模拟”实现一些对象接口。在测试过程中,这些虚构的占位符对象 可用简单方式来模仿对于一个组件的期望的行为和结果,从而让你专注于组件本身的彻底测试而不用担心其它依赖性问题。
Spring从J2EE的Web端为每个关键接口提供了一个mock实现:
MockHttpServletRequest—几乎每个单元测试中都要使用这个类,它是J2EE Web应用程序最常用的接口HttpServletRequest的mock实现。
MockHttpServletResponse—此对象用于HttpServletResponse接口的mock实现。
MockHttpSession—这是另外一个经常使用的mock对象(后文将讨论此类在会话绑定处理中的应用)。
DelegatingServletInputSt
DelegatingServletOutputS
总之,在实现你自己的测试控制器时,上面这些对象是最为有用的。然而,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框架扩展。
二、普通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 testInvokesCorrectMethod
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();
viewInvoiceAsPDFControll
byte[] responsePDFValues = response.getContentAsByteArray();
byte[] expectedPDFValues = loadBytesFromTestFile();
assertTrue( "Did not generate expected PDF content.", Arrays.equals(responsePDFValues,expectedPDFValues ));
}
注意,在此你的控制器ViewInvoiceAsPDFControll
四、事务性单元测试
到目前为止,你已看到了相对简单的JUnit测试—它仅发生在用mock对象支持的一个控制 器的上下文中。但是,如果测试一个Web组件只有在一个事务性上下文(例如,通过依赖性注入与Hibernate集成到一起)中才有意义的情况又会怎么样 呢?不必担心,Spring MVC为JUnit框架提供了一个体面的扩展集合—它能准确地提供依赖性注入和事务安全测试(也就是,任何更新在测试完成后都将被回滚)。
测试步骤:
让我们看一种假想的情形—你要实现一个组件(例如MyTransactionalControlle
1.创建一个定制的JUnit类(MyTransactionalControlle
import org.springframework.test.AbstractDependencyInject
public class MyTransactualControllerT
public class.
2.为了实现从Spring内置的单元测试中发现Spring管理的bean,你需要重载getConfigLocations()方法并且返回上下文文件位置的String数组,请看如下:
protected abstract String[] getConfigLocations(){ return new String[] {"classpath:/test/spring-context.xml"};}
3.拥有该类的一个测试属性及其相关联的getter和setter。由于 AbstractTransactionalSpr
public MyTransactualController myTransactualController;
public MyTransactualController getMyTransactualControll
{
return this.myTransactualController;
}
public void setMyTransactualControll
}
4.就象你通常操作“普通的”JUnit测试一样实现测试方法:
public void testCorrectBehavior() throws Exception{ //运行该事务性方法 myTransactualController.submitPayment( new Payment( 100 ) );
assertTrue( myTransactualController.isValid() );}
注意,你是在调用可能会更新数据库的方法submitPayment。Spring的JUnit扩展(AbstractTransactionalSpr
5.如果你需要执行任何安装或清除任务,则可以重载 AbstractTransactionalSpr