高手都這么給Spring MVC做單元測(cè)試!
作者:alanshelby
來(lái)源:zhuanlan.zhihu.com/p/43260823
「一、前言」
在前面的章節(jié)我們介紹過(guò) Junit 的使用,也了解過(guò) spring-test,今天我們來(lái)了解一個(gè)新玩意 -- mock 測(cè)試。這里僅僅做一個(gè)入門(mén),對(duì)返回視圖和返回 Json 數(shù)據(jù)的方法進(jìn)行測(cè)試演示,不會(huì)把所有的方法都介紹到,具體文檔詳見(jiàn)鏈接:Mock Test,本章節(jié)主要講解以下兩部分內(nèi)容:
1、Mock 測(cè)試簡(jiǎn)介
2、測(cè)試用例演示
二、Mock 測(cè)試簡(jiǎn)介
1、什么是 mock 測(cè)試
在測(cè)試過(guò)程中,對(duì)于某些不容易構(gòu)造或者不容易獲取的對(duì)象,用一個(gè)「虛擬的對(duì)象」來(lái)創(chuàng)建以便測(cè)試的測(cè)試方法,就是 「mock 測(cè)試」在測(cè)試過(guò)程中,對(duì)于某些不容易構(gòu)造或者不容易獲取的對(duì)象,用一個(gè)「虛擬的對(duì)象」來(lái)創(chuàng)建以便測(cè)試的測(cè)試方法,就是mock 測(cè)試。
- 虛擬的對(duì)象就是 mock 對(duì)象。
- mock 對(duì)象就是真實(shí)對(duì)象在調(diào)試期間的代替品。
2、為什么使用 mock 測(cè)試
- 避免開(kāi)發(fā)模塊之間的耦合
- 輕量、簡(jiǎn)單、靈活
3、MockMVC 介紹
基于 RESTful 風(fēng)格的 SpringMVC 的測(cè)試,我們可以測(cè)試完整的 Spring MVC 流程,即從 URL 請(qǐng)求到控制器處理,再到視圖渲染都可以測(cè)試。
1)MockMvcBuilder
MockMvcBuilder 是用來(lái)構(gòu)造 MockMvc 的構(gòu)造器,其主要有兩個(gè)實(shí)現(xiàn):StandaloneMockMvcBuilder 和 DefaultMockMvcBuilder,對(duì)于我們來(lái)說(shuō)直接使用「靜態(tài)工廠 MockMvcBuilders 創(chuàng)建」即可。
2)MockMvcBuilders
負(fù)責(zé)創(chuàng)建 MockMvcBuilder 對(duì)象,有兩種創(chuàng)建方式:
standaloneSetup(Object... controllers):通過(guò)參數(shù)指定一組控制器,這樣就不需要從上下文獲取了。
「webAppContextSetup」(WebApplicationContext wac):指定 WebApplicationContext,將會(huì)從該上下文獲取相應(yīng)的控制器并得到相應(yīng)的 MockMvc,本章節(jié)下面測(cè)試用例均使用這種方式創(chuàng)建 MockMvcBuilder 對(duì)象。
3)MockMvc
對(duì)于服務(wù)器端的 SpringMVC 測(cè)試支持主入口點(diǎn)。通過(guò) MockMvcBuilder 構(gòu)造 MockMvcBuilder 由 MockMvcBuilders 建造者的靜態(tài)方法去建造。
核心方法:perform(RequestBuilder rb) -- 執(zhí)行一個(gè) RequestBuilder 請(qǐng)求,會(huì)自動(dòng)執(zhí)行 SpringMVC 的流程并映射到相應(yīng)的控制器執(zhí)行處理,該方法的返回值是一個(gè) ResultActions。
4)ResultActions
(1)「andExpect」:添加 ResultMatcher 驗(yàn)證規(guī)則,驗(yàn)證控制器執(zhí)行完成后結(jié)果是否正確;
(2)「andDo」:添加 ResultHandler 結(jié)果處理器,比如調(diào)試時(shí)打印結(jié)果到控制臺(tái);
(3)「andReturn」:最后返回相應(yīng)的 「MvcResult」;然后進(jìn)行自定義驗(yàn)證 / 進(jìn)行下一步的異步處理;
5)MockMvcRequestBuilders
用來(lái)構(gòu)建請(qǐng)求的,其主要有兩個(gè)子類 「MockHttpServletRequestBuilder *和」 MockMultipartHttpServletRequestBuilder*(如文件上傳使用),即用來(lái) Mock 客戶端請(qǐng)求需要的所有數(shù)據(jù)。
6)MockMvcResultMatchers
(1)用來(lái)匹配執(zhí)行完請(qǐng)求后的「結(jié)果驗(yàn)證」
(2)如果匹配失敗將拋出相應(yīng)的異常
(3)包含了很多驗(yàn)證 API 方法
7)MockMvcResultHandlers
(1)結(jié)果處理器,表示要對(duì)結(jié)果做點(diǎn)什么事情
(2)比如此處使用 MockMvcResultHandlers.print() 輸出整個(gè)響應(yīng)結(jié)果信息
8)MvcResult
(1)單元測(cè)試執(zhí)行結(jié)果,可以針對(duì)執(zhí)行結(jié)果進(jìn)行「自定義驗(yàn)證邏輯」。
三、測(cè)試用例演示
1、添加依賴
org.springframeworkspring-test5.0.7.RELEASEjunitjunit4.12com.jayway.jsonpathjson-path2.2.0
前兩個(gè) jar 依賴我們都已經(jīng)接觸過(guò)了,對(duì)于返回視圖方法的測(cè)試這兩個(gè) jar 依賴已經(jīng)足夠了,第三個(gè) jar 依賴是用于處理返回 Json 數(shù)據(jù)方法的,這里要明白每個(gè) jar 的具體作用。
2、被測(cè)試的方法
@RequestMapping(value = "editItem")
public String editItem(Integer id, Model model) {
Item item = itemService.getItemById(id);
model.addAttribute("item", item); return "itemEdit";
}
@RequestMapping(value = "getItem")
@ResponseBody
public Item getItem(Integer id) {
Item item = itemService.getItemById(id); return item;
}
這里我們提供了兩個(gè)方法,一個(gè)是返回視圖的方法,另一個(gè)是返回 Json 數(shù)據(jù)的方法,下面我們會(huì)給出測(cè)試類,分別對(duì)這兩個(gè)方法進(jìn)行測(cè)試。
3、測(cè)試類:ItemMockTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/*.xml")
@WebAppConfiguration
public class ItemMockTest {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void init() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
}
這里前兩個(gè)注解就不再解釋了,我們?cè)趯W(xué)習(xí) Spring 與 Junit 整合的時(shí)候已經(jīng)講解過(guò)了,這里說(shuō)一下第三個(gè)注解:@WebAppConfiguration:可以在單元測(cè)試的時(shí)候,不用啟動(dòng) Servlet 容器,就可以獲取一個(gè) Web 應(yīng)用上下文。
1)返回視圖方法測(cè)試
@Test
public void test() throws Exception {
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/editItem").param("id", "1"))
.andExpect(MockMvcResultMatchers.view().name("itemEdit"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn();
Assert.assertNotNull(result.getModelAndView().getModel().get("item"));
}
img
這三句代碼是我們對(duì)結(jié)果的期望,最后打印出了結(jié)果,說(shuō)明執(zhí)行成功,所有期望都達(dá)到了,否則會(huì)直接報(bào)錯(cuò)。從結(jié)果中我們就可以看到這個(gè)請(qǐng)求測(cè)試的情況。
2、返回 Json 數(shù)據(jù)方法
@Test
public void test1() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/getItem")
.param("id", "1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.jsonPath("$.id").value(1))
.andExpect(MockMvcResultMatchers.jsonPath("$.name").value("IPhone X"))
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
img
在這個(gè)方法中比較特殊的就是設(shè)置 MediaType 類型,因?yàn)槎际鞘褂?Json 格式,所以設(shè)置了 MediaType.APPLICATION_JSON,jsonPath 用于比對(duì)期望的數(shù)據(jù)是否與返回的結(jié)果一致,這里需要注意的是 "$.id" 這 key 的種形式。
四、小結(jié)
這里只是用到了 MockMvc 很小一部分知識(shí),更加深入學(xué)習(xí)會(huì)使你養(yǎng)成一種良好編寫(xiě)單元測(cè)試的習(xí)慣,這是十分難得的一種好習(xí)慣,推薦去看官方文檔,然后動(dòng)手去測(cè)試一下,為你編寫(xiě)的每一個(gè) Controller 方法進(jìn)行測(cè)試,保證他們的可靠性。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!