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

本文探讨了在Java Spring框架下,使用MockMvc和RestTemplate进行Controller单元测试的区别。重点在于事务控制的有效性,指出MockMvc能有效利用@Transactional进行事务回滚,而RestTemplate则无法做到。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alphathur

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值