Spring MVC使用MockMvc和Junit进行单元测试
声明一点:对于软件质量的保证,单元测试真的非常有必要,在CMMI-5标准也是对单元测试有明确要求的。
有的公司的对开发要求严谨的话,开发流程可能执行的是测试驱动开发(TDD
),即根据拿到需求,开发人员先写预期单元测试用例,再根据满足测试用例的要求来开发业务代码。
在不启动项目的情况下,对Spring Web项目,需要一些Servlet
相关的模拟对象,比如:MockMvc,MockHttpSession,MockHttpServletRequest,MockHttpServletResponse
等,还需要WebAppConfiguration
。
示例代码
引入依赖
1 | <!-- Sprign test --> |
Controller
1 | import org.springframework.beans.factory.annotation.Autowired; |
Service
1 | import org.springframework.stereotype.Service; |
单元测试代码
Maven管理的Spring项目,单元测试代码放在src/test/java
目录里
@RunWith(SpringJUnit4ClassRunner.class)
:让测试运行于 Spring-test 测试环境。@WebAppConfiguration
:指定加载的ApplicationContext
是个WebAppConfiguration
。@ContextConfiguration
:加载配置类或XML配置文件。@Transactional
:用于事务回滚,将测试数据删除掉,保证数据库数据干净。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
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;
import com.config.SpringMvcConfig;
import com.service.impl.DemoServiceImpl;
/**
* Mockmvc测试数据回滚:
* 1. 继承AbstractTransactionalJUnit4SpringContextTests类是实现数据自动回滚
* 2. 使用@Transactional
* 3. 使用@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
* @author Rocky
*
*/
//声明加载WebApplicationContext
public class TestController extends AbstractTransactionalJUnit4SpringContextTests {
private MockMvc mockMvc;
private DemoServiceImpl demoServiceImpl;//在测试代码中注入Spring的Bean
WebApplicationContext webApplicationContext; //注入WebApplicationContext
MockHttpSession session;//注入模拟http session
MockHttpServletRequest request;//注入模拟http request
MockHttpServletResponse response; //注入模拟http response
/**
* 在测试开始前初始化
*/
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
}
/**
* 向normal进行get请求,预控制返回200
* 预期view名为page
* @throws Exception
*/
public void testNormalController() throws Exception {
mockMvc.perform(get("/normal"))
.andExpect(status().isOk())
.andExpect(view().name("page"))
.andExpect(forwardedUrl("/WEB-INF/jsp/page.jsp"))
.andExpect(model().attribute("msg", demoServiceImpl.saySomething()));
}
public void testRestController() throws Exception {
mockMvc.perform(get("/testRest"))
.andExpect(status().isOk())
.andExpect(content().contentType("text/plain;charset=UTF-8"))
.andExpect(content().string(demoServiceImpl.saySomething()));
}
}page.jsp页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="${pageContext.request.contextPath}/static/js/jquery-1.11.0.js"></script>
<title>Test Page</title>
</head>
<body>
<pre>Welcome to Spring MVC World</pre>
</body>
</html>MockMvc原理分析
MockMvc执行测试步骤:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void testNormalController() throws Exception {
//链式编程,需导入静态包
mockMvc.perform(get("/normal"))
.andExpect(status().isOk())
.andExpect(view().name("page"))
.andExpect(forwardedUrl("/WEB-INF/jsp/page.jsp"))
.andExpect(model().attribute("msg", demoServiceImpl.saySomething()));
//1. 构造一个请求
RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/normal");
//2. 执行请求
ResultActions resultActions = mockMvc.perform(requestBuilder);
//3. 结果匹配
ResultMatcher isOK = MockMvcResultMatchers.status().isOk();//返回的状态
ResultMatcher viewName = MockMvcResultMatchers.view().name("page");//view name
ResultMatcher viewURL = MockMvcResultMatchers.forwardedUrl("/WEB-INF/jsp/page.jsp");//物理视图
ResultMatcher attribute = MockMvcResultMatchers.model().attribute("msg", demoServiceImpl.saySomething());//封装参数
//4. 执行完成后的断言
resultActions.andExpect(isOK)
.andExpect(viewName)
.andExpect(viewURL)
.andExpect(attribute);
}- 测试之前执行初始化
MockMvcBuilder
是用来构造MockMvc
的构造器,其主要有两个实现:StandaloneMockMvcBuilder
和DefaultMockMvcBuilder
,StandaloneMockMvcBuilder
继承了DefaultMockMvcBuilder
。 直接使用静态工厂MockMvcBuilders
创建即可。 **MockMvcBuilders.webAppContextSetup(WebApplicationContext context)**:指定 WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc
。
原理分析: MockMvcBuilders 构造器是重点,webAppContextSetup()
方法是通过DefaultMockMvcBuilder
构造方法返回一个包含WebApplicationContext
的对象。
1
2
3public static DefaultMockMvcBuilder webAppContextSetup(WebApplicationContext context) {
return new DefaultMockMvcBuilder(context);
}
DefaultMockMvcBuilder
继承了AbstractMockMvcBuilder
,提供了一API。
- **addFilters(Filter… filters)/addFilter(Filter filter, String… urlPatterns)**:添加javax.servlet.Filter过滤器。
- **defaultRequest(RequestBuilder requestBuilder)**:默认的 RequestBuilder
,每次执行时会合并到自定义的 RequestBuilder中,即提供公共请求数据的。
- **alwaysExpect(ResultMatcher resultMatcher)**:定义全局的结果验证器,即每次执行请求时都进行验证的规则。
- **alwaysDo(ResultHandler resultHandler)**:定义全局结果处理器,即每次请求时都进行结果处理。
- dispatchOptions:DispatcherServlet
是否分发 OPTIONS
请求方法到控制器。
- 其它见上方代码注释:2,3,4。
ResultActions.andDo
添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()
输出整个响应结果信息。ResultActions.andReturn
表示执行完成后返回相应的结果。
Spring MVC使用MockMvc和Junit进行单元测试
http://blog.gxitsky.com/2018/02/24/SpringMVC-34-test-spring-mockmv/