从源码上学习 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.classCookieResultMatchers.classFlashAttributeResultMatchers.classHandlerResultMatchers.classHeaderResultMatchers.classJsonPathResultMatchers.classMockMvcResultHandlers.classMockMvcResultMatchers.classModelResultMatchers.classPrintingResultHandler.classRequestResultMatchers.classStatusResultMatchers.classStatusResultMatchersExtensionsKt.classViewResultMatchers.classXpathResultMatchers.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@WebAppConfigurationpublic 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 加入
还未添加个人简介










评论