易测性(在框架中测试每个组件而不管其具体种类)是Spring框架所提倡的关键原则之一。本文集中探讨Spring的易测性特征—它能使得对Web组件进行单元测试就象测试普通Java对象(POJO)一样容易。
(二)会话相关的操作
对于任何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组件的开发效率。
查看本文来源