The Spring MVC Test framework provides first class JUnit support for testing client and server-side Spring MVC code through a fluent API. Typically it loads the actual Spring configuration through theTestContext framework and always uses the
DispatcherServlet
to process requests thus approximating full integration tests without requiring a running Servlet container.
Spring MVC 测试框架本来是一个独立的项目,由于发展的很好,早已合并到Spring Framework 3.2 里了,测试框架提供了很好的API来测试客户端和服务端的Spring MVC代码, 本文以两个例子讲述了服务端的测试,闲话少说,让我们边看例子边学习。
目录
Getting Ready
Example
Reference Class
Unit Test
Integration Testing
总结
Troubleshooting
参考
Getting Ready
测试相关Maven dependency如下:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.0.3.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.9.5</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
关于Spring项目的一些依赖如(spring-context, spring-web, spring-webmvc, spring-beans),这里就不列举了
Example
Reference Class
Controller 如下:
@Controller @RequestMapping("/") public class DemoController { @Autowired private TestProjectService testProjectService; @RequestMapping(value = "jsonCompare", method = RequestMethod.POST) @ResponseBody public List<FieldComparisonFailure> jsonCompare(@RequestParam("expect") String expect, @RequestParam("actual") String actual, ModelMap model, HttpSession session) { List<FieldComparisonFailure> list = testProjectService.compare(expect, actual); return list; } }
FieldComparisonFailure类如下
/** * Models a failure when comparing two fields. */ public class FieldComparisonFailure { private final String field; private final Object expected; private final Object actual; public FieldComparisonFailure(String field, Object expected, Object actual) { this.field = field; this.expected = expected; this.actual = actual; } public String getField() { return field; } public Object getExpected() { return expected; } public Object getActual() { return actual; } }
TestProjectService接口如下:
public interface TestProjectService { public List<FieldComparisonFailure> compare(String expect, String actual); }
TestProjectServiceImpl 具体实现是比较两个Json字符串 返回一个List
@Service public class TestProjectServiceImpl implements TestProjectService { @Override public List<FieldComparisonFailure> compare(String expect, String actual) { Comparator comparator = new Comparator(); List<FieldComparisonFailure> list = new ArrayList<FieldComparisonFailure>(); try { list = comparator.compare(expect, actual); } catch (JSONException e) { e.printStackTrace(); } return list; } }
##转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html
Unit Test
首先来看一个独立的单元测试方式, 这个例子用Mockito 模拟service层以便隔离controller的测试。
package com.wadeshop.controller; import static org.mockito.Mockito.when; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.hamcrest.Matchers.hasSize; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import com.google.common.collect.ImmutableList; import com.wadeshop.service.TestProjectService; import com.wadeshop.entity.FieldComparisonFailure; public class DemoControllerTest_mock { @Mock private TestProjectService testProjectService; @InjectMocks private DemoController demoController; private MockMvc mockMvc; @Before public void setup() { // initialize mock object MockitoAnnotations.initMocks(this); // Setup Spring test in standalone mode this.mockMvc = MockMvcBuilders.standaloneSetup(demoController).build(); } @Test public void test() throws Exception { //prepare test data FieldComparisonFailure e1 = new FieldComparisonFailure("Number", "3", "4"); FieldComparisonFailure e2 = new FieldComparisonFailure("Number", "1", "2"); //actually parameter haven't use, service was mocked String expect = ""; String actual = ""; //Sets a return value to be returned when the method is called when(testProjectService.compare(expect, actual)).thenReturn(ImmutableList.of(e1, e2)); //construct http request and expect response this.mockMvc .perform(post("/jsonCompare") .accept(MediaType.APPLICATION_JSON) .param("actual", actual) .param("expect", expect)) .andDo(print()) //print request and response to Console .andExpect(status().isOk()) .andExpect(content().contentType("application/json;charset=UTF-8")) .andExpect(jsonPath("$", hasSize(2))) .andExpect(jsonPath("$[0].field").value("Number")) .andExpect(jsonPath("$[0].expected").value("3")) .andExpect(jsonPath("$[0].actual").value("4")) .andExpect(jsonPath("$[1].field").value("Number")) .andExpect(jsonPath("$[1].expected").value("1")) .andExpect(jsonPath("$[1].actual").value("2")); //verify Interactions with any mock verify(testProjectService, times(1)).compare(expect, actual); verifyNoMoreInteractions(testProjectService); } }
@Mock: 需要被Mock的对象
@InjectMocks: 需要将Mock对象注入的对象, 此处就是Controller
Before test
初始化Mock对象, 通过MockMvcBuilders.standaloneSetup模拟一个Mvc测试环境,注入controller, 通过build得到一个MockMvc, 后面就用MockMvc的一些API做测试。
这不是真实的Spring MVC环境,如果要模拟真实环境需要用 MockMvcBuilders.webAppContextSetup(webApplicationContext).build(), 见下文。
测试方法里面需要构建测试数据,mock service调用方法,返回一个ImmutableList (google-collections 谷歌的集合库)
然后构造http请求并且传入参数执行, 最后断言验证期望结果, 关于JsonPath的使用请参考http://goessner.net/articles/JsonPath/
运行
andDo(print()) 打印到控制台的信息如下
MockHttpServletRequest: HTTP Method = POST Request URI = /jsonCompare Parameters = {actual=[], expect=[]} Headers = {Accept=[application/json]} Handler: Type = com.wadeshop.controller.DemoController Async: Was async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = null View = null Model = null FlashMap: MockHttpServletResponse: Status = 200 Error message = null Headers = {Content-Type=[application/json;charset=UTF-8]} Content type = application/json;charset=UTF-8 Body = [{"field":"Number","actual":"4","expected":"3"},{"field":"Number","actual":"2","expected":"1"}] Forwarded URL = null Redirected URL = null Cookies = []
##转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html
Integration Testing
再来看集成Web环境方式, 这次仍然使用Spring MVC Test 但还需要加载 WebApplicationContext
import static org.hamcrest.Matchers.hasSize; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration(value = "src/main/webapp") @ContextConfiguration("file:src/main/resources/applicationContext.xml") public class DemoControllerTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setUp() { mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @Test public void test() throws Exception { String actual = "{\"orderNumber\": \"4955\",\"orderVersion\": \"1\"}"; String expect = "{\"orderNumber\": \"4956\",\"orderVersion\": \"1\"}"; this.mockMvc .perform(post("/jsonCompare") .accept(MediaType.APPLICATION_JSON) .param("actual", actual) .param("expect", expect)) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$", hasSize(1))) .andExpect(jsonPath("$[0].field").value("orderNumber")) .andExpect(jsonPath("$[0].actual").value("4955")) .andExpect(jsonPath("$[0].expected").value("4956")); } }
@RunWith: 告诉Junit使用 Spring-Test 框架, 允许加载web 应用程序上下文。
@WebAppConfiguration: 表明该类会使用web应用程序的默认根目录来载入ApplicationContext, value = "src/main/webapp" 可以不填,默认此目录
@ContextConfiguration: 指定需要加载的spring配置文件的地址 ("file:src/main/resources/applicationContext.xml")
@Autowired WebApplicationContext wac:注入web环境的ApplicationContext容器;
使用MockMvcBuilders.webAppContextSetup(wac).build()来创建一个MockMvc进行测试, 此为模拟真实的Spring MVC环境
测试过程和前面一个例子大体相似,唯一的区别就是,这次传入的是真实的参数,调用真实的service取得返回值。
运行时间比较长
控制台信息
MockHttpServletRequest: HTTP Method = POST Request URI = /jsonCompare Parameters = {actual=[{"orderNumber": "4955","orderVersion": "1"}], expect=[{"orderNumber": "4956","orderVersion": "1"}]} Headers = {Accept=[application/json]} Handler: Type = com.wadeshop.controller.DemoController Async: Was async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = null View = null Model = null FlashMap: MockHttpServletResponse: Status = 200 Error message = null Headers = {Content-Type=[application/json]} Content type = application/json Body = [{"field":"orderNumber","actual":"4955","expected":"4956"}] Forwarded URL = null Redirected URL = null Cookies = []
从上面的例子来看集成测试Spring MVC controller是不是也很简单, 稍微配置一下就行了。
##转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html
总结
单元测试过程无非就这三部曲:
- 准备 (测试环境,测试数据)
- 执行 (构造请求传入参数执行)
- 断言 (验证结果)
Troubleshooting
如果发现一些NoClassDefFoundError, 估计依赖的jar版本太旧了。
import 哪些类不要弄错了,有些需要static import, IDE工具不一定会提示导入
参考
MVC测试框架更多的API请参考这篇博客:http://jinnianshilongnian.iteye.com/blog/2004660
相关推荐
Table of Contents Configuring a Spring Development Environment Spring Framework Fundamentals Web Application Architecture Spring MVC Architecture Implementing Controllers Implementing Controllers ...
Spring MVC helps you build flexible and loosely coupled web applications. The Spring MVC Framework is architected and designed in such a way that every piece of logic and functionality is highly ...
Configure Spring MVC to build logic-less controllers that transparently support the most advanced web techniques Build an amazing social and financial application that applies microservices patterns ...
Chapter 9: Testing Spring MVC Applications ......................................................273 Chapter 10: Spring Web Flow ....................................................................
Spring Boot Testing部分尤其强调了如何使用Spring Boot进行自动化测试,包括单元测试和集成测试。 Spring Boot是Spring生态中的一个关键组件,它简化了基于Spring的应用程序的配置和部署。Spring Boot Intro部分会...
8. **测试(Testing)**:由于Go语言内置了强大的测试工具,GoMVC框架也会提供方便的测试接口,帮助开发者编写和运行单元测试。 学习和理解GoMVC框架的源码,不仅可以深入掌握Golang的Web开发,还可以借鉴其设计...
7.1. Alternate Spring MVC configuration 7.1.1. Customizing DispatcherServlet configuration 7.1.2. Adding additional servlets and filters 7.1.3. Declaring DispatcherServlet in web.xml 7.2. Processing ...
Build full-featured web applications such as Spring MVC applications efficiently that will get you up and running with Spring web development Learn dependency injection and aspect-oriented programming...
8. **测试(Testing)**:Spring Boot提供了测试支持,包括单元测试和集成测试,确保系统的正确性。 9. **持久化技术(Persistence Technology)**:外卖系统可能使用MySQL、PostgreSQL等关系型数据库存储数据,也...
10. **Testing**: Spring Boot提供了测试支持,包括单元测试和集成测试,开发者可以通过`@SpringBootTest`等注解进行测试。 这个项目是一个很好的起点,帮助开发者了解Spring Boot的基本架构和工作原理,通过实际...
7. **测试(Testing)**:确保代码质量,JUnit和Mockito等工具用于单元测试,Spring Boot也提供了集成测试的支持。 8. **文档(Documentation)**:使用Swagger或Javadoc生成API的在线文档,便于开发者理解和使用。...
在这个模块中,我们可以使用Spring Boot提供的测试工具和库来对Web应用程序进行有效的单元测试和集成测试。 首先,让我们了解一下Spring Boot的测试支持。Spring Boot Test模块提供了多种测试注解,如`@SpringBoot...
8. **测试(Testing)**:应用可能包含JUnit或Spring Boot的`@SpringBootTest`注解的测试类,用于确保各组件正常工作。 在开发过程中,开发者可能会利用Spring Data JPA进行ORM(对象关系映射),简化数据库操作。...
3.9. Testing Improvements ........................................................................................ 20 III. Core Technologies ..............................................................
6. **测试(Testing)**:项目中可能包含JUnit单元测试和Spring MVC的集成测试,确保API的正确性和稳定性。 7. **配置管理(Configuration Management)**:使用Spring Boot的配置文件(application.properties或...
10. **测试(Testing)**:Spring Boot 提供了丰富的测试支持,包括单元测试、集成测试以及端到端测试。`@WebMvcTest`、`@SpringBootTest`等注解可以帮助快速编写针对REST服务的测试。 在Spring-REST-main这个项目中...
项目包含了丰富的单元测试和集成测试,确保代码的正确性和稳定性。JUnit 和 Mockito 是常用的 Java 测试库,它们可以帮助开发者编写和执行测试用例。 9. **Spring MVC**: Spring MVC 是 Spring 框架的一部分,...