使用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<PageInfo<HyCardSearchResult>> res = Utils
.getRootLoginTemplate(testRestTemplate)
.exchange("/card/collects", HttpMethod.POST,
new HttpEntity<>(ImmutableMap.of("cardId", 20, "userId", 1, "companyId", "123")),
new ParameterizedTypeReference<PageInfo<HyCardSearchResult>>() {
});
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,运行结果:
可以看出,它实际上插入了数据。
删除这条数据,再执行CardControllerMockmvcTest,运行结果:
可以看出它并没有插入数据!
尽管两个测试类都添加了@Transactional对事务进行回滚,但是使用RestTemplate的测试类,这个注解实际上是无效的。
所以:如果你的Controller单元测试对事务有要求,请使用MockMvc而不是RestTemplate。
还没有评论,来说两句吧...