使用MockMvc来代替RestTemplate对Controller进行单元测试

怼烎@ 2021-12-21 18:27 480阅读 0赞

对Controller进行单元测试可以通过RestTemplat发送一个http请求来实现。也可以通过MockMvc来实现,二者还是有很大区别的,参考Difference between MockMvc and RestTemplate in integration tests

简单来说,二者的直接区别是:

使用MockMvc,通常是设置一个完整的Web应用程序上下文,来模拟HTTP请求和响应,它创建的是一个假的DispatcherServlet来模拟MVC堆栈的运行方式,没有真正的网络连接。

使用RestTemplate,部署的是一个实际的Web服务器来监听您发送的HTTP请求,并响应结果。

但是二者还有个非常重要的差异:

使用MockMvc可以对Controller进行事务控制,即@Transactional在MockMvc的单元测试中是有效的。但是使用RestTemplate创建网络连接的测试,无法对Controller进行事务控制,即@Transactional在RestTemplate的单元测试中是无效的。

以下是代码演示:

原始的Controller:

  1. @RestController
  2. @RequestMapping("card")
  3. public class CardController {
  4. private final CardCollectService cardCollectService;
  5. @PostMapping("collects")
  6. public HyCardCollect createCollect(@RequestBody HyCardCollect cardCollect) {
  7. return cardCollectService.createHyCardCollect(cardCollect);
  8. }
  9. }

使用RestTemplate对Controller做单元测试:

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
  3. @Transactional
  4. @Slf4j
  5. public class CardControllerRestTest {
  6. @Autowired
  7. private TestRestTemplate testRestTemplate;
  8. @Test
  9. public void testCardCollect() {
  10. ResponseEntity<PageInfo<HyCardSearchResult>> res = Utils
  11. .getRootLoginTemplate(testRestTemplate)
  12. .exchange("/card/collects", HttpMethod.POST,
  13. new HttpEntity<>(ImmutableMap.of("cardId", 20, "userId", 1, "companyId", "123")),
  14. new ParameterizedTypeReference<PageInfo<HyCardSearchResult>>() {
  15. });
  16. log.debug("resp status={}", res.getStatusCode());
  17. Assert.assertEquals(res.getStatusCode(), HttpStatus.OK);
  18. }
  19. }

也可以使用MockMvc对Controller做单元测试,下面是个标准的范例:

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. @Transactional
  4. @Slf4j
  5. public class CardControllerMockmvcTest {
  6. @Autowired
  7. private WebApplicationContext context;
  8. private MockMvc mvc;
  9. @Before
  10. public void setUp() throws Exception {
  11. mvc = MockMvcBuilders.webAppContextSetup(context).build();//建议使ç¨è¿ç§
  12. }
  13. @Test
  14. public void testCardCollect() throws Exception {
  15. HyCardCollect collect = new HyCardCollect();
  16. collect.setCardId(20);
  17. collect.setUserId(1);
  18. collect.setCompanyId("123");
  19. MockHttpServletResponse response = mvc.perform(MockMvcRequestBuilders.post("/card/collects")
  20. .contentType(MediaType.APPLICATION_JSON_VALUE)
  21. .content(JSON.toJSONString(collect)))
  22. .andDo(MockMvcResultHandlers.print())
  23. .andReturn()
  24. .getResponse();
  25. Assert.assertEquals(HttpStatus.OK.value(), response.getStatus());
  26. }
  27. }

执行CardControllerRestTest,运行结果:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21yeWFuZzEyNQ_size_16_color_FFFFFF_t_70

可以看出,它实际上插入了数据。

删除这条数据,再执行CardControllerMockmvcTest,运行结果:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21yeWFuZzEyNQ_size_16_color_FFFFFF_t_70 1

可以看出它并没有插入数据!

尽管两个测试类都添加了@Transactional对事务进行回滚,但是使用RestTemplate的测试类,这个注解实际上是无效的。

所以:如果你的Controller单元测试对事务有要求,请使用MockMvc而不是RestTemplate。

发表评论

表情:
评论列表 (有 0 条评论,480人围观)

还没有评论,来说两句吧...

相关阅读

    相关 MockMvc 单元测试

     MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以