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

对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:

@RestController
@RequestMapping("card")
public class CardController {

  private final CardCollectService cardCollectService;

  @PostMapping("collects")
  public HyCardCollect createCollect(@RequestBody HyCardCollect cardCollect) {
    return cardCollectService.createHyCardCollect(cardCollect);
  }

}

使用RestTemplate对Controller做单元测试: 

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@Transactional
@Slf4j
public class CardControllerRestTest {

  @Autowired
  private TestRestTemplate testRestTemplate;

  @Test
  public void testCardCollect() {
    ResponseEntity> res = Utils
        .getRootLoginTemplate(testRestTemplate)
        .exchange("/card/collects", HttpMethod.POST,
            new HttpEntity<>(ImmutableMap.of("cardId", 20, "userId", 1, "companyId", "123")),
            new ParameterizedTypeReference>() {
            });
    log.debug("resp status={}", res.getStatusCode());
    Assert.assertEquals(res.getStatusCode(), HttpStatus.OK);
  }
}

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

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
@Slf4j
public class CardControllerMockmvcTest {

  @Autowired
  private WebApplicationContext context;
  private MockMvc mvc;

  @Before
  public void setUp() throws Exception {
    mvc = MockMvcBuilders.webAppContextSetup(context).build();//建议使ç¨è¿ç§
  }

  @Test
  public void testCardCollect() throws Exception {
    HyCardCollect collect = new HyCardCollect();
    collect.setCardId(20);
    collect.setUserId(1);
    collect.setCompanyId("123");

    MockHttpServletResponse response = mvc.perform(MockMvcRequestBuilders.post("/card/collects")
        .contentType(MediaType.APPLICATION_JSON_VALUE)
        .content(JSON.toJSONString(collect)))
        .andDo(MockMvcResultHandlers.print())
        .andReturn()
        .getResponse();
    Assert.assertEquals(HttpStatus.OK.value(), response.getStatus());
  }
}

执行CardControllerRestTest,运行结果:

使用MockMvc来代替RestTemplate对Controller进行单元测试_第1张图片

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

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

使用MockMvc来代替RestTemplate对Controller进行单元测试_第2张图片

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

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

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

你可能感兴趣的:(Java基础)