从源码上学习 MockMvc 的使用
- 2022 年 7 月 21 日
本文字数:3882 字
阅读完需:约 13 分钟
步骤:
新建一个测试类
给测试类添加类注解
@RunWith(SpringRunner.class) @SpringBootTest @WebAppConfiguration
注入私有属性
@Autowire private WebApplicationContext context
;定义一个私有的共用属性
private MockMvc mock
;编写一个 Junit 测试的前置方法(在测试方法前执行,做准备工作,
@Before
)
@Before
public void before() throws Exception {
mock = MockMvcBuilders.webAppContextSetup(context).build(); }
对
controller
中的方法进行测试(示例测试UserController_findAll
)
@Test
public void testFindAll(){
MvcResult result = mock.perform( MockMvcRequestBuilders.get('/user'))
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
一般使用形式
ResultActions action = mock.perform(RequestBuilder);
action.andExpect(ResultMatcher)
.andDo(ResultHandler)
.andReturn();
interface RequestBuilder
构建 request 请求
需要使用的方法,可以在其实现类 MockHttpServletRequestBuilder
中查阅到
摘抄部分源码
/**
* Set the character encoding of the request.
* @param encoding the character encoding
*/
public MockHttpServletRequestBuilder characterEncoding(String encoding) {
this.characterEncoding = encoding;
return this;
}
/**
* Set the request body.
* @param content the body content
*/
public MockHttpServletRequestBuilder content(byte[] content) {
this.content = content;
return this;
}
/**
* Set the request body as a UTF-8 String.
* @param content the body content
*/
public MockHttpServletRequestBuilder content(String content) {
this.content = content.getBytes(StandardCharsets.UTF_8);
return this;
}
/**
* Set the 'Content-Type' header of the request.
* @param contentType the content type
*/
public MockHttpServletRequestBuilder contentType(MediaType contentType) {
Assert.notNull(contentType, "'contentType' must not be null");
this.contentType = contentType.toString();
return this;
}
常用方法
contentType(MediaType);
content(String);
param(String, String...);
requestAttr(String, Object);
accept(MediaType);
headers(HttpHeaders);
interfact ResultMatcher
函数式接口,对结果,进行条件匹配
在 org.springframework.test.web.servlet.result
包下的 *ResultMatchers
中,定义了绝大部分的返回 ResultMatcher
的匿名实现类的方法
# 所有的类
ContentResultMatchers.class
CookieResultMatchers.class
FlashAttributeResultMatchers.class
HandlerResultMatchers.class
HeaderResultMatchers.class
JsonPathResultMatchers.class
MockMvcResultHandlers.class
MockMvcResultMatchers.class
ModelResultMatchers.class
PrintingResultHandler.class
RequestResultMatchers.class
StatusResultMatchers.class
StatusResultMatchersExtensionsKt.class
ViewResultMatchers.class
XpathResultMatchers.class
以 .andExpect(status().isOk())
为案例追踪源码
// StatusResultMatchers.isOk()
/**
* Assert the response status code is {@code HttpStatus.OK} (200).
*/
public ResultMatcher isOk() {
return matcher(HttpStatus.OK);
}
/**
* Match the expected response status to that of the HttpServletResponse.
*/
private ResultMatcher matcher(final HttpStatus status) {
return result -> assertEquals("Status", status.value(), result.getResponse().getStatus());
}
在 .andExpect(ResultMatcher)中的参数,其实,通过 MockMvcResultMatchers 中的静态方法,去生成对应的 Matcher
//
/**
* Access to response status assertions.
*/
public static StatusResultMatchers status() {
return new StatusResultMatchers();
}
interface ResultHandler
函数式接口,对获得的结果,进行处理
常见的处理为输出到日志,或输出到控制台,或输出到指定的输出流
推荐使用 MockMvcResultHandlers
中的方法,从源码可得知,MockMvcResultHandlers
中定义了私有的静态内部类对 PrintingResultHandler
和 ResultHandler
进行了扩展
// 可以使用的 MockMvcResultHandlers 中的静态方法
log();
print();
print(OutputStream);
print(Writer);
完整的测试示例
@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class StationTest {
@Autowired
private WebApplicationContext context;
private MockMvc mock;
private String baseUrl = "/station";
private List<Station> list;
private MvcResult result;
private String id;
@Before
public void before() throws Exception {
mock = MockMvcBuilders.webAppContextSetup(context).build();
list = JsonUtil.toList(
mock.perform(get(baseUrl)).andReturn().getResponse().getContentAsString(),
Station.class);
System.err.println("\n==============================================================================\n");
id = list.get(0).getId();
}
@After
public void after() throws Exception {
SimpleLogUtil.error(result.getResponse().getContentAsString());
}
@Test
public void testFindAll() throws Exception {
result = mock.perform(MockMvcRequestBuilders.get(baseUrl))
.andExpect(status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn();
list = JsonUtil.toList(result.getResponse().getContentAsString(), Station.class);
id = list.get(0).getId();
SimpleLogUtil.error(list);
}
@Test
public void testSave() throws Exception {
Station station = new Station(RCSUtil.CreateID(), "station-1", 0, 1, "2", "1", 20, "10");
String json = JsonUtil.toJson(station);
result = mock
.perform(MockMvcRequestBuilders.post(baseUrl)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(json))
.andExpect(status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
@Test
public void testGet() throws Exception {
result = mock.perform(MockMvcRequestBuilders.get(baseUrl + "/{id}", list.get(0).getId()))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
@Test
public void testUpdate() throws Exception {
Station station = list.get(0);
station.setName("st-123");
station.setWalkType("0");
result = mock
.perform(MockMvcRequestBuilders.post(baseUrl + "/{id}", id)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(JsonUtil.toJson(station)))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
@Test
public void testDelete() throws Exception {
result = mock
.perform(MockMvcRequestBuilders.delete(baseUrl + "/{id}", id))
.andExpect(status().isOk())
.andExpect(content().string("true"))
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
}
@RestController
@RequestMapping("/station")
public class StationController {
@GetMapping("")
public List<Station> findAll() {
return service.findAll();
}
@PostMapping("")
public Station save(@RequestBody Station station) {
return service.save(station);
}
@GetMapping("/{id}")
public Station get(@PathVariable String id) {
return service.getById(id);
}
@PostMapping("/{id}")
public Station update(@RequestBody Station station, @PathVariable String id) {
station.setId(id);
return service.save(station);
}
@DeleteMapping("/{id}")
public boolean delete(@PathVariable String id) {
return service.delete(id);
}
@Autowired
private StationService service;
}
版权声明: 本文为 InfoQ 作者【安逸的咸鱼】的原创文章。
原文链接:【http://xie.infoq.cn/article/3be9b760e3034a142e51c53a6】。文章转载请联系作者。
安逸的咸鱼
CV 工程师 2022.07.13 加入
还未添加个人简介
评论