java單元測試_mock系列

這篇文章很詳細的介紹了什麼是Mock

Springboot 單元測試之Mock_springboot mock_生萬千歡喜心的博客-CSDN博客

快速入門mock

快速入門JAVA單元測試——mock_java單元測試mock_水中加點糖的博客-CSDN博客

學會使用mock做單元測試

教你使用Mock完成單元測試 - 知乎 (zhihu.com)

java單元測試之mock測試編寫

java單元測試之Mock測試編寫_mock文檔-CSDN博客

springboot測試之mock/mockmvc

SpringBoot測試之mock--mock-MVC_spring mock mvc_吧啦蹦吧的博客-CSDN博客

mockito使用教程

mockito使用教程_mockitoannotations.initmocks_jack-life的博客-CSDN博客

mockito三種實現方式

 快速入門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具體使用方法見文檔

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的時候,返回user
    doReturn(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的時候,返回user
    when(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返回true
    when(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測試編寫

 

 

 

Springboot 單元測試之Mock

 

 

 

 

 

 

 

 

 

 

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