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