Java单元测试使用mock【转载】
1、什么是Mock?
mock是在測試過程中,對于一些不容易構造/獲取的對象,創建一個mock對象來模擬對象的行為。比如說你需要調用B服務,可是B服務還沒有開發完成,那么你就可以將調用B服務的那部分給Mock掉,并編寫你想要的返回結果。 Mock有很多的實現框架,例如Mockito、EasyMock、Jmockit、PowerMock、Spock等等,SpringBoot默認的Mock框架是Mockito,和junit一樣,只需要依賴spring-boot-starter-test就可以了。本文代碼基于jdk8、junit5、Mockito3
1.1、 Mockito中文文檔
Mockito是mocking框架,它讓你用簡潔的API做測試。而且Mockito簡單易學,它可讀性強和驗證語法簡潔。Mockito是GitHub上使用最廣泛的Mock框架,并與JUnit結合使用.Mockito框架可以創建和配置mock對象.使用Mockito簡化了具有外部依賴的類的測試開發! Mockito具體使用方法見文檔https://github.com/hehonghui/mockito-doc-zh#0
1.2、Mockito基本使用方法簡介
1)、靜態導入會使代碼更簡潔
import static org.mockito.Mockito.*;舉例:
//創建mock對象,mock一個List接口 List mockedList = mock(List.class); //如果不使用靜態導入,則必須使用Mockito調用 List mockList = Mockito.mock(List.class);2)、驗證某些行為
//你可以mock一個具體的類型,而不僅是接口 LinkedList mockedList = mock(LinkedList.class);mockedList.add("one");//驗證 verify(mockedList).add("one");一旦mock對象被創建了,mock對象會記住所有的交互。然后你就可能選擇性的驗證你感興趣的交互。
3)、如何做一些測試樁
//測試樁 when(mockedList.get(0)).thenReturn("first"); when(mockedList.get(1)).thenThrow(new RuntimeException());當調用mockList.get(0)的時候,返回first
當調用mockList.get(1)的時候,拋出一個運行時異常
4)、其他使用見上面文檔
2、MockMVC基于RESTful風格的測試
對于前后端分離的項目而言,無法直接從前端靜態代碼中測試接口的正確性,因此可以通過MockMVC來模擬HTTP請求。基于RESTful風格的SpringMVC的測試,我們可以測試完整的Spring MVC流程,即從URL請求到控制器處理,再到視圖渲染都可以測試。
2.1、初始化MockMvc對象
@Autowired private WebApplicationContext webApplicationContext; private MockMvc mockMvc;//在每個測試方法執行之前都初始化MockMvc對象 @BeforeEach public void setupMockMvc() {mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); }2.2、完成一些接口的測試
1)、嘗試測試一個不存在的請求 /user/1
/*** @DisplayName 自定義測試方法展示的名稱* @throws Exception*/ @DisplayName("測試根據Id獲取User") @Test void contextLoads() throws Exception {//perform,執行一個RequestBuilders請求,會自動執行SpringMVC的流程并映射到相應的控制器執行處理mockMvc.perform(MockMvcRequestBuilders//構造一個get請求.get("/user/1")//請求類型 json.contentType(MediaType.APPLICATION_JSON))// 期待返回的狀態碼是4XX,因為我們并沒有寫/user/{id}的get接口.andExpect(MockMvcResultMatchers.status().is4xxClientError());}展示結果:
2)、在Controller中完成 /user/{id}
/*** id:\\d+只匹配數字* @param id* @return*/ @GetMapping("/user/{id:\\d+}") public User getUserById(@PathVariable Long id) {return userService.getById(id); }修改一下測試類:期待返回的結果是200
@Test void getUserById() throws Exception {//perform,執行一個RequestBuilders請求,會自動執行SpringMVC的流程并映射到相應的控制器執行處理mockMvc.perform(MockMvcRequestBuilders//構造一個get請求.get("/user/1")//請求類型 json.contentType(MediaType.APPLICATION_JSON))// 期望的結果狀態 200.andExpect(MockMvcResultMatchers.status().isOk()); }結果展示:
3)、我們可以把結果打印到控制臺
// 期望的結果狀態 200 .andExpect(MockMvcResultMatchers.status().isOk()) //添加ResultHandler結果處理器,比如調試時 打印結果(print方法)到控制臺 .andDo(MockMvcResultHandlers.print());運行結果:可以看到并沒有返回結果
4)、結合Mockito構建自定義返回結果
這里就用到了Mockito的應用場景,userService.getById并沒有返回結果,但是我們的測試并不關心userService.getById這個方法是否正常,只是在我們的測試中需要用到這個方法,所以我們可以Mock掉UserService的getById方法,自己定義返回的結果,繼續我們的測試。
@MockBean private UserService userService;@Test void getUserById() throws Exception {User user = new User();user.setId(1);user.setNickname("yunqing");//Mock一個結果,當userService調用getById的時候,返回userdoReturn(user).when(userService).getById(any());//perform,執行一個RequestBuilders請求,會自動執行SpringMVC的流程并映射到相應的控制器執行處理mockMvc.perform(MockMvcRequestBuilders//構造一個get請求.get("/user/1")//請求類型 json.contentType(MediaType.APPLICATION_JSON))// 期望的結果狀態 200.andExpect(MockMvcResultMatchers.status().isOk())//添加ResultHandler結果處理器,比如調試時 打印結果(print方法)到控制臺.andDo(MockMvcResultHandlers.print()); }運行結果
5)、傳參數
@Test void getUserByUsername() throws Exception {// perform : 執行請求 ;mockMvc.perform(MockMvcRequestBuilders//MockMvcRequestBuilders.get("/url") : 構造一個get請求.get("/user/getUserByName")//傳參.param("username","admin")// 請求type : json.contentType(MediaType.APPLICATION_JSON))// 期望的結果狀態 200.andExpect(MockMvcResultMatchers.status().isOk()); }6)、期望返回結果集有兩個元素
@Test void getAll() throws Exception {User user = new User();user.setNickname("yunqing");List<User> list = new LinkedList<>();list.add(user);list.add(user);//Mock一個結果,當userService調用list的時候,返回userwhen(userService.list()).thenReturn(list);//perform,執行一個RequestBuilders請求,會自動執行SpringMVC的流程并映射到相應的控制器執行處理mockMvc.perform(MockMvcRequestBuilders//構造一個get請求.get("/user/list")//請求類型 json.contentType(MediaType.APPLICATION_JSON))// 期望的結果狀態 200.andExpect(MockMvcResultMatchers.status().isOk())//期望返回的結果集合有兩個元素.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(2))//添加ResultHandler結果處理器,比如調試時 打印結果(print方法)到控制臺.andDo(MockMvcResultHandlers.print()); }運行結果:
7)、測試Post請求
@Test void insert() throws Exception {User user = new User();user.setNickname("yunqing");String jsonResult = JSONObject.toJSONString(user);//直接自定義save返回truewhen(userService.save(any())).thenReturn(true);// perform : 執行請求 ;MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders//MockMvcRequestBuilders.post("/url") : 構造一個post請求.post("/user/insert").accept(MediaType.APPLICATION_JSON)//傳參,因為后端是@RequestBody所以這里直接傳json字符串.content(jsonResult)// 請求type : json.contentType(MediaType.APPLICATION_JSON))// 期望的結果狀態 200.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn();//返回結果int statusCode = mvcResult.getResponse().getStatus();String result = mvcResult.getResponse().getContentAsString();//單個斷言Assertions.assertEquals(200, statusCode);//多個斷言,即使出錯也會檢查所有斷言assertAll("斷言",() -> assertEquals(200, statusCode),() -> assertTrue("true".equals(result)));3、一些常用API總結
常用的期望:
//使用jsonPaht驗證返回的json中code、message字段的返回值 .andExpect(MockMvcResultMatchers.jsonPath("$.code").value("00000")) .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("成功")) //body屬性不為空 .andExpect(MockMvcResultMatchers.jsonPath("$.body").isNotEmpty()) // 期望的返回結果集合有2個元素 , $: 返回結果 .andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(2));附帶常用API解釋:
RequestBuilder/MockMvcRequestBuilders: //根據uri模板和uri變量值得到一個GET請求方式的MockHttpServletRequestBuilder; MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables) //同get類似,但是是POST方法; MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables) //同get類似,但是是PUT方法; MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables) //同get類似,但是是DELETE方法; MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) //同get類似,但是是OPTIONS方法; MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables) //提供自己的Http請求方法及uri模板和uri變量,如上API都是委托給這個API; MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables) //提供文件上傳方式的請求,得到MockMultipartHttpServletRequestBuilder; MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables) //創建一個從啟動異步處理的請求的MvcResult進行異步分派的RequestBuilder; RequestBuilder asyncDispatch(final MvcResult mvcResult)MockHttpServletRequestBuilder:
//:添加頭信息; MockHttpServletRequestBuilder header(String name, Object... values)/MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders) //:指定請求的contentType頭信息; MockHttpServletRequestBuilder contentType(MediaType mediaType) //:指定請求的Accept頭信息; MockHttpServletRequestBuilder accept(MediaType... mediaTypes)/MockHttpServletRequestBuilder accept(String... mediaTypes) //:指定請求Body體內容; MockHttpServletRequestBuilder content(byte[] content)/MockHttpServletRequestBuilder content(String content) //:請求傳入參數 MockHttpServletRequestBuilder param(String name,String... values) //:指定請求的Cookie; MockHttpServletRequestBuilder cookie(Cookie... cookies) //:指定請求的Locale; MockHttpServletRequestBuilder locale(Locale locale) //:指定請求字符編碼; MockHttpServletRequestBuilder characterEncoding(String encoding) //:設置請求屬性數據; MockHttpServletRequestBuilder requestAttr(String name, Object value) //:設置請求session屬性數據; MockHttpServletRequestBuilder sessionAttr(String name, Object value)/MockHttpServletRequestBuilder sessionAttrs(Map<string, object=""> sessionAttributes) //指定請求的flash信息,比如重定向后的屬性信息; MockHttpServletRequestBuilder flashAttr(String name, Object value)/MockHttpServletRequestBuilder flashAttrs(Map<string, object=""> flashAttributes) //:指定請求的Session; MockHttpServletRequestBuilder session(MockHttpSession session) // :指定請求的Principal; MockHttpServletRequestBuilder principal(Principal principal) //:指定請求的上下文路徑,必須以“/”開頭,且不能以“/”結尾; MockHttpServletRequestBuilder contextPath(String contextPath) //:請求的路徑信息,必須以“/”開頭; MockHttpServletRequestBuilder pathInfo(String pathInfo) //:請求是否使用安全通道; MockHttpServletRequestBuilder secure(boolean secure) //:請求的后處理器,用于自定義一些請求處理的擴展點; MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor)MockMultipartHttpServletRequestBuilder
//:指定要上傳的文件; MockMultipartHttpServletRequestBuilder file(String name, byte[] content)/MockMultipartHttpServletRequestBuilder file(MockMultipartFile file)ResultActions
//:添加驗證斷言來判斷執行請求后的結果是否是預期的; ResultActions andExpect(ResultMatcher matcher) //:添加結果處理器,用于對驗證成功后執行的動作,如輸出下請求/結果信息用于調試; ResultActions andDo(ResultHandler handler) //:返回驗證成功后的MvcResult;用于自定義驗證/下一步的異步處理; MvcResult andReturn()ResultMatcher/MockMvcResultMatchers
//:請求的Handler驗證器,比如驗證處理器類型/方法名;此處的Handler其實就是處理請求的控制器; HandlerResultMatchers handler() //:得到RequestResultMatchers驗證器; RequestResultMatchers request() //:得到模型驗證器; ModelResultMatchers model() //:得到視圖驗證器; ViewResultMatchers view() //:得到Flash屬性驗證; FlashAttributeResultMatchers flash() //:得到響應狀態驗證器; StatusResultMatchers status() //:得到響應Header驗證器; HeaderResultMatchers header() //:得到響應Cookie驗證器; CookieResultMatchers cookie() //:得到響應內容驗證器; ContentResultMatchers content() //:得到Json表達式驗證器; JsonPathResultMatchers jsonPath(String expression, Object ... args)/ResultMatcher jsonPath(String expression, Matcher matcher) //:得到Xpath表達式驗證器; XpathResultMatchers xpath(String expression, Object... args)/XpathResultMatchers xpath(String expression, Map<string, string=""> namespaces, Object... args) //:驗證處理完請求后轉發的url(絕對匹配); ResultMatcher forwardedUrl(final String expectedUrl) //:驗證處理完請求后轉發的url(Ant風格模式匹配,@since spring4); ResultMatcher forwardedUrlPattern(final String urlPattern) //:驗證處理完請求后重定向的url(絕對匹配); ResultMatcher redirectedUrl(final String expectedUrl) //:驗證處理完請求后重定向的url(Ant風格模式匹配,@since spring4); ResultMatcher redirectedUrlPattern(final String expectedUrl)原文鏈接
總結
以上是生活随笔為你收集整理的Java单元测试使用mock【转载】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 淘宝自动回复机器人配置手册——售前模板功
- 下一篇: 阿里面试真题:Dubbo的SPI机制