文章目錄
- 基礎
- 基礎概念
- Mockxxx
- 一般實現
- 文件位置
- 實戰
- MockMvc與Test注解不兼容
- @RequestParams參數
- @RequestBody參數
基礎
基礎概念
定義:是Spring框架提供的一種用于測試Spring MVC控制器的工具,它允許開發者在不啟動完整的web服務器的情況下,模擬HTTP請求并驗證響應。優點:執行速度快 --》 不需要啟動web服務器;便于集成 --》 可以與Junit、TestNG等測試框架無縫銜接;強大的功能 --》 對HTTP請求的詳細配置和響應的全面驗證;依賴配置:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
Mockxxx
MockMvc:來自于:import org.springframework.test.web.servlet.MockMvc;定義:是Spring Test模塊的一部分,它允許開發者對Spring MVC控制器進行單元測試而無需啟動完整的Web服務器;通過MockMvc,可以模擬HTTP請求并驗證響應,使得測試執行速度更快,同時便于與JUnit、TestNG等測試框架集成。使用:使用mockMvc。perform()模擬HTTP請求,使用.andExpect()和.andReturn()等方法進行響應驗證。
Mockito:來自于:import org.mockito.Mockito;定義:流行的Java單元測試框架,專門用于創建和驗證模擬對象的行為。它允許開發者在編寫測試時模擬外部依賴,從而使得測試更便捷,減少對外部類、系統和依賴給單元測試帶來的耦合。特點:行為驗證、測試樁、參數匹配器、注冊支持、監控真實對象、重置mock對象;
MvcResult:來自于:import org.springframework.test.web.servlet.MvcResult;定義:是在執行模擬HTTP請求以測試控制器(Controller)層功能時的重要概念,它代表一個完整的HTTP響應結果,包括響應的狀態碼、頭信息、響應體以及任何可能產生的錯誤等。作用:封裝HTTP響應 -》封裝由模擬的HTTP產生的完整響應信息;獲取響應細節 -》包括響應的狀態碼、頭信息、響應體等內容;斷言測試 -》 使用MvcResult中的方法進行斷言,確保控制器返回正確的HTTP狀態碼和數據;
一般實現
// 類注解,用于配置 JUnit 5 測試類以使用 Spring 的測試支持。
@ExtendWith(SpringExtension.class)// 是一個 Spring Boot 提供的注解,用于對 Web 層(即控制器層)進行測試。如果使用該注解,那么Spring Boot 會自動配置一個模擬的 Spring MVC 環境,這樣就可以在不啟動完整應用的情況下測試控制器的行為。
@WebMvcTest(value = {Controller.class, Handler.class})// 完成bean自動裝配
@Autowired
private MockMvc mockMvc;// 標識測試方法
@Test
MockHttpServletRequestBuilder requestBuilder = get("控制類路徑/xxx/xxx");
// MockHttpServletRequestBuilder 是一個用于構建模擬HTTP請求的工具類。
// 主要在單元測試和集成測試中使用,構造出特定的HTTP請求來測試控制器(Controller)或端點(Endpoint)。
requestBuilder.param("參數名", 參數值);
// 給已創建的mockhttpservletrequestbuilder實例添加查詢參數;
// 將參數通過鍵值對的形式填入MockHttpServletRequestBuilder對象中;mockMvc.perform(requestBuilder) // mockMvc對象執行,之前定義的requestbuiler對象.andExpect(status().isOk()) // 該調用方式是鏈式調用;--》 用于檢查HTTP響應的狀態碼是否是200.andDo(new ResultHandler() { // 自定義處理MvcResult對象,即模擬請求后的結果@Overridepublic void handle(MvcResult mvcResult) throws Exception {// 獲取響應體并將其轉化為字符串類型;String content = mvcResult.getResponse().getContentAsString();// 檢查響應體是否為空,以確保有數據返回;assertTrue(StringUtils.isNotBlank(content));// 將響應體內容解析成map集合Map<String, String> resp = JSONUtils.toMap(content);// 驗證code/message 兩個屬性的值是否等于1/success;assertEquals("1", resp.get("code"));assertEquals("success", resp.get("message"));// 獲取data屬性對應的值String data = resp.get("data");// base64解碼byte[] encryptedData = Base64.decode(data);// aes密鑰生成AES aes = genAES(keyIv);// 解密data對應的值String json = new String(aes.decrypt(encryptedData), StandardCharsets.UTF_8);// json format is ResponseKeyCollection 自定義的一個類ResponseKeyCollection collection = JSONUtils.toObject(json, ResponseKeyCollection.class);// 將json轉成 ResponseKeyCollection對象assertEquals(10, collection.getResponseKeys().size());// 驗證對象中getResponseKeys方法返回的集合大小是否為10}});@BeforeEach
ResponseKey responseKey1 = COLLECTION.getResponseKeys().stream().filter(key -> key.getIndex() == 1).findFirst().orElse(null);
// COLLECTION獲取ResponseKeys屬性--》轉換為流--》過濾出index=1的responseKey對象
// --》查找第一個滿足條件的--》有則返回,無則返回null;
// COLLECTION 是自定義的對象
Mockito.when(keyStoreService.getResponseKey(1)).thenReturn(responseKey1);
// 模擬該方法,當此入參為1時,則返回上述找到的對象;
Mockito.when(keyStoreService.generateResponseKeyCollection(10)).thenReturn(COLLECTION);
// 模擬該方法,當此入參為10時,則返回Collecion對象;
Mockito.when(keyHelper.isExpiredKeyIndex(anyInt())).thenReturn(false);
// 模擬此方法,不管該方法入參為誰,都返回false --》 即keyIndex永不過期
文件位置
與項目目錄保持一致;
假設項目文件為src/main/java/xxx/controller/xxxController
測試類項目文件為src/test/java/xxx/controller/xxxTest
實戰
MockMvc與Test注解不兼容
參考博客:https://segmentfault.com/q/1010000042943340
描述:使用 @Autowiredprivate MockMvc mockMvc;總是導致注入的mockMvc失敗;
原因是:MockMvc與@Test不兼容的問題;原本依賴庫為:org.junit.Test 改成org.junit.jupiter.api.Test 就可以了;兩個依賴庫之間的關系為:org.junit.Test --》 JUnit4 org.junit.jupiter.api.Test --》 JUnit5
@RequestParams參數
MockHttpServletRequestBuilder requestBuilder = get("/decrypt");
requestBuilder.param("id1", id1);
requestBuilder.param("id2", id2);
mockMvc.perform(requestBuilder).andExpect(status().isOk()).andDo(mvcResult -> {String content = mvcResult.getResponse().getContentAsString();
});
@RequestBody參數
接口請求參數:
@RequestMapping("/getfunction1")
public ResultDtoRisk riskGetTokenByph(HttpServletRequest request, @RequestBody NumReq numReq){}測試代碼:
@Autowired
pivate MockMvc mockMvc;
// import org.springframework.test.web.servlet.MockMvc;NumReq numReq = new NumReq();
numReq.setId1(id1);
numReq.setId2(id2);MockHttpServletRequestBuilder requestBuilder = get("/getfunction1");
// import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
String requestBodyContent = objectMapper.writeValueAsString(numReq);requestBuilder.contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBodyContent);mockMvc.perform(requestBuilder).andExpect(status().isOk()).andDo(print());