写点什么

从源码上学习 MockMvc 的使用

作者:安逸的咸鱼
  • 2022 年 7 月 21 日
  • 本文字数:3882 字

    阅读完需:约 13 分钟

步骤:

  1. 新建一个测试类

  2. 给测试类添加类注解 @RunWith(SpringRunner.class) @SpringBootTest @WebAppConfiguration

  3. 注入私有属性 @Autowire private WebApplicationContext context;

  4. 定义一个私有的共用属性 private MockMvc mock;

  5. 编写一个 Junit 测试的前置方法(在测试方法前执行,做准备工作,@Before


@Before public void before() throws Exception { mock = MockMvcBuilders.webAppContextSetup(context).build(); }
复制代码


  1. 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 中定义了私有的静态内部类对 PrintingResultHandlerResultHandler 进行了扩展


// 可以使用的 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;}
复制代码


发布于: 刚刚阅读数: 5
用户头像

CV 工程师 2022.07.13 加入

还未添加个人简介

评论

发布
暂无评论
从源码上学习 MockMvc 的使用_Java_安逸的咸鱼_InfoQ写作社区