对于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组件的开发效率。
相关推荐
JUnit作为Java领域最流行的单元测试框架,与Spring MVC和Spring Boot的集成使得开发者能够更高效地进行测试驱动开发(TDD)或行为驱动开发(BDD)。下面将详细介绍这个主题中的相关知识点。 1. JUnit JUnit是一个...
在Spring Boot框架中,单元测试是一项至关重要的任务,它能够帮助开发者确保代码的正确性和可靠性。Spring Boot提供了方便的工具和库,使得编写和执行单元测试变得简单高效。本章节我们将深入探讨Spring Boot中的...
JUnit 是Java编程语言中最广泛使用的单元测试框架,它为开发者提供了一种简洁、高效的方式来编写和运行测试用例,以验证代码的功能是否符合预期。本篇将深入探讨JUnit的使用以及在Spring MVC中的应用。 首先,我们...
`@MockBean`用于创建模拟对象等,这些都极大地简化了测试的编写和执行。 "spring-test-3.2.8"引入了一些重要的改进,比如支持Spring 3.2的主要特性,增强了对异步测试的支持,以及对RESTful Web服务测试的改进。...
1. **Spring Test**:这是 Spring 框架的一个模块,提供了一组工具和类,帮助开发者编写和运行针对 Spring 应用的测试。它支持单元测试、集成测试,并且与多种测试框架如 JUnit、TestNG 集成。主要特性包括: - `@...
1. `Mockito`:一个流行的模拟框架,允许在测试中创建和配置mock对象,隔离被测试代码。 2. `JUnit`:一个广泛使用的单元测试框架,Spring Test与JUnit深度集成,提供测试注解如`@Test`,`@Before`,`@After`等。 3....
4. **Mock对象与Spy对象**: 在JUnit和Spring测试中,经常使用Mockito等库创建mock对象来模拟依赖,以便测试特定组件的行为。Spy对象则是mock对象的一种变体,允许部分真实行为的执行。 5. **Spring Boot ...
Junit作为Java编程中最常用的单元测试框架,也是书中不可或缺的一部分,用于验证Spring组件的功能正确性。下面我们将详细探讨Spring框架和Junit在软件开发中的重要性以及如何结合使用它们。 Spring框架是Java企业级...
在Spring框架中,JUnit是广泛使用的单元测试工具,它与Spring的集成使得我们可以方便地进行基于Java的测试。本文将深入探讨如何在Spring环境中利用JUnit进行"Hello, World!"的测试,同时也涉及到一些源码分析和测试...
Spring Boot是Java生态系统中的一个流行框架,用于简化Spring应用程序的创建和配置,而JUnit是Java开发中最常用的单元测试库。下面,我们将详细阐述这两个技术的结合以及如何进行测试。 首先,让我们了解Spring ...
Spring框架是Java开发中的一个核心组件,主要用于简化企业级应用的开发。`spring-test.zip`文件包含的是关于Spring测试的相关代码示例,这对于理解和掌握Spring框架的测试支持至关重要。Spring Test模块提供了集成...
在单元测试中,我们有时需要模拟(mock)外部依赖,Spring提供了`@MockBean`和`@SpyBean`注解,可以在测试环境中创建mock对象,避免真实依赖对测试的影响。 10. **SpringTest**: 这可能是项目中的测试类或者一个...
`spring-web.jar`和`spring-webmvc.jar`是Web上下文和MVC框架的实现,用于构建基于Spring的应用服务器端组件。 9. **Spring Test**: 提供了对Spring应用程序进行单元测试和集成测试的支持。`spring-test.jar`包含了...
首先,`spring-test`模块的核心目标是简化Spring应用的测试工作,它提供了对JUnit、TestNG等主流测试框架的集成,使得开发者能够便捷地进行单元测试和集成测试。在`3.2.0.RELEASE`这个版本中,Spring Test进一步增强...
- **Mock对象**:在SpringBoot-Junit项目中,可能使用Mockito来模拟依赖,以便在测试中隔离被测组件。 - **@Autowired与@MockBean**:`@Autowired`用于注入实际的Bean,而`@MockBean`则会替换掉指定的Bean,创建一...
Struts2框架是一款广泛应用于Java Web开发中的开源MVC框架,它简化了Web应用程序的构建,使得业务逻辑、控制逻辑和视图层得以分离。单元测试对于任何软件项目都至关重要,因为它能确保代码的正确性,提高代码质量...
8. **测试支持**:Spring提供了强大的单元测试和集成测试支持,包括Mock对象、TestContext框架和JUnit集成,帮助开发者编写可测试的代码。 9. **Spring Boot**:虽然不在Spring 2的范围内,但Spring Boot是基于...
Spring支持使用JUnit等测试框架,同时也提供了Mock对象、测试切面等工具。 Spring框架支持多版本的JDK和JavaEE。在Spring 5.0.0中,引入了对JDK 8+的特性支持,如Lambda表达式和新的时间API。Spring 5的发布也标志...
在IT行业中,Spring Boot是一个非常流行的Java框架,用于简化微服务和Web应用的开发。它提供了许多开箱即用的功能,使得开发者可以快速构建可运行的应用。本示例程序是关于"Spring Boot Test"的实践,它展示了如何...
9. **spring-test**: 提供了测试支持,包括Mock对象和JUnit集成,帮助开发者进行单元测试和集成测试。 除了这些核心模块,`spring-framework-5.3.13`还可能包含其他模块,如用于消息传递的`spring-messaging`,用于...