在之前的 Spring学习之旅(八)--SpringMVC请求参数 我们是通过在控制台输出来验证参数是否正确,但是这样做实在是太耗时间了,我们今天来学习下 MockMvc,它可以让我们不需要启动项目就能调用接口并验证接口返回结果是否符合我们的预期。
为何使用MockMvc?
MockMvc 实现了对 Http 请求的模拟,能够直接使用网络的形式,实现 Controller 的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。
如何使用 MockMvc
MockMvcBuilder 是用来构造 MockMvc 的构造器,主要有两个实现:
- StandaloneMockMvcBuilder
- DefaultMockMvcBuilder
分别对应两种测试方式,即独立安装和集成 Web 环境测试(此种方式并不会集成真正的 web 环境,而是通过相应的 Mock API 进行模拟测试,无须启动服务器)。对于我们来说直接使用静态工厂 MockMvcBuilders 创建即可。
集成Web环境方式
MockMvcBuilders.webAppContextSetup(WebApplicationContext context)
:指定 WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的 MockMvc;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RootConfig.class)
@WebAppConfiguration
public class RequestParameterControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
}
独立测试方式
MockMvcBuilders.standaloneSetup(Object... controllers)
:通过参数指定一组控制器,这样就不需要从上下文获取了;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RootConfig.class)
@WebAppConfiguration
public class RequestParameterControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new RequestParameterController()).build();
}
}
实例
Controller:
@Controller
public class RequestParameterController {
@RequestMapping("/toInt")
@ResponseBody
public int toInt(int value) {
return value;
}
}
单元测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RootConfig.class)
@WebAppConfiguration
public class RequestParameterControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void toInt() throws Exception {
mockMvc.perform(
// 发送 GET 请求
MockMvcRequestBuilders.get("/toInteger?value=3"))
// 判断HTTP响应码
.andExpect(MockMvcResultMatchers.status().isOk())
// 判断返回内容是否是预期值
.andExpect(MockMvcResultMatchers.content().string("3"))
// 输出整个响应结果信息
.andDo(MockMvcResultHandlers.print());
}
}
控制台输出:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /toInteger
Parameters = {value=[3]}
Headers = {}
Handler:
Type = com.marklogzhu.web.controller.RequestParameterController
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[text/plain;charset=ISO-8859-1], Content-Length=[1]}
Content type = text/plain;charset=ISO-8859-1
Body = 3
Forwarded URL = null
Redirected URL = null
Cookies = []
Process finished with exit code 0
常用对象
MockMvcRequestBuilders
MockMvcRequestBuilders: 用来构建请求。
方法 | 作用 |
---|---|
MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables) | 发送 GET 请求 |
MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables) | 发送 POST 请求 |
MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables) | 发送 PUT 请求 |
MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) | 发送 DELETE 请求 |
MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables) | 发送 OPTIONS 请求 |
MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables) | 提供自己的Http请求方法及uri模板和uri变量 |
MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables) | 发送文件上传请求 |
RequestBuilder asyncDispatch(final MvcResult mvcResult) | 创建一个启动异步处理的请求的 MvcResult 进行异步分派的RequestBuilder |
ResultActions
调用 MockMvc.perform(RequestBuilder requestBuilder)
后将得到 ResultActions,通过 ResultActions 可以完成如下三件事:
- ResultActions andExpect(ResultMatcher matcher) :添加验证断言来判断执行请求后的结果是否是预期的;
- ResultActions andDo(ResultHandler handler) :添加结果处理器,用于对验证成功后执行的动作,如输出下请求/结果信息用于调试;
- MvcResult andReturn() :返回验证成功后的MvcResult;用于自定义验证/下一步的异步处理;
ResultMatcher
方法 | 作用 |
---|---|
HandlerResultMatchers handler() | 请求的 Handler 验证器,比如验证处理器类型/方法名;此处的 Handler 其实就是处理请求的控制器 |
RequestResultMatchers request() | 得到 RequestResultMatchers 验证器 |
ModelResultMatchers model() | 得到模型验证器 |
ViewResultMatchers view() | 得到视图验证器 |
FlashAttributeResultMatchers flash() | 得到 Flash 属性验证 |
StatusResultMatchers status() | 得到响应状态验证器 |
HeaderResultMatchers header() | 得到响应 Header 验证器 |
CookieResultMatchers cookie() | 得到响应 Cookie 验证器 |
ContentResultMatchers content() | 得到响应内容验证器 |
JsonPathResultMatchers jsonPath(String expression, Object ... args) | 得到Json表达式验证器 |
ResultMatcher jsonPath(String expression, Matcher matcher) | 得到Json表达式验证器 |
XpathResultMatchers xpath(String expression, Object... args) | 得到Xpath表达式验证器 |
XpathResultMatchers xpath(String expression, Map<string, string=""> namespaces, Object... args) | 得到Xpath表达式验证器 |
ResultMatcher forwardedUrl(final String expectedUrl) | 验证处理完请求后转发的url(绝对匹配) |
ResultMatcher forwardedUrlPattern(final String urlPattern) | 验证处理完请求后转发的url(Ant风格模式匹配,@since spring4) |
ResultMatcher redirectedUrl(final String expectedUrl) | 验证处理完请求后重定向的url(绝对匹配) |
ResultMatcher redirectedUrlPattern(final String expectedUrl) | 验证处理完请求后重定向的url(Ant风格模式匹配,@since spring4) |
实例
JSON请求/响应验证
String requestBody = "{\"id\":1, \"name\":\"zhang\"}";
mockMvc.perform(post("/user")
.contentType(MediaType.APPLICATION_JSON).content(requestBody)
.accept(MediaType.APPLICATION_JSON)) //执行请求
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) //验证响应contentType
.andExpect(jsonPath("$.id").value(1)); //使用Json path 验证JSON,具体表达式规则请参考 http://goessner.net/articles/JsonPath/
String errorBody = "{id:1, name:zhang}";
MvcResult result = mockMvc.perform(post("/user")
.contentType(MediaType.APPLICATION_JSON).content(errorBody)
.accept(MediaType.APPLICATION_JSON)) //执行请求
.andExpect(status().isBadRequest()) //400错误请求
.andReturn();
Assert.assertTrue(HttpMessageNotReadableException.class.isAssignableFrom(result.getResolvedException().getClass()));//错误的请求内容体
文件上传
byte[] bytes = new byte[] {1, 2};
mockMvc.perform(fileUpload("/user/{id}/icon", 1L).file("icon", bytes)) //执行文件上传
.andExpect(model().attribute("icon", bytes)) //验证属性相等性
.andExpect(view().name("success")); //验证视图
自定义验证
MvcResult result = mockMvc.perform(get("/user/{id}", 1))//执行请求
.andReturn(); //返回MvcResult
Assert.assertNotNull(result.getModelAndView().getModel().get("user")); //自定义断言