使用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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章