首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Spring MVC 測試必備:MockMvc 靜態導入最佳實踐

     編輯:Java 綜合教學

Spring MVC 測試必備:MockMvc 靜態導入最佳實踐

使用 Spring Test 框架進行 Controller 單元測試時,MockMvc 是核心工具。靜態導入關鍵方法能大幅簡化測試代碼,提升可讀性。本文整理最常用的靜態導入模式與實戰技巧。

核心靜態導入

// 請求構建器 - 核心入口
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;

// 結果斷言 - 核心驗證
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

// 結果處理器 - 列印/調試
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;

// 設置/構建器
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;

常用請求構建方法

方法說明範例
get(url)GET 請求get("/users/1")
post(url)POST 請求post("/users").content(json)
put(url)PUT 請求put("/users/1").content(json)
delete(url)DELETE 請求delete("/users/1")
patch(url)PATCH 請求patch("/users/1").content(json)
request(HttpMethod, url)通用請求request(HttpMethod.OPTIONS, "/")
fileUpload(url)檔案上傳fileUpload("/upload").file(mockFile)

請求修飾鏈式調用

// 完整請求構建範例
mockMvc.perform(
    post("/api/users")
        .contentType(MediaType.APPLICATION_JSON)           // Content-Type
        .content(objectMapper.writeValueAsString(user))    // Request Body
        .header("Authorization", "Bearer " + token)        // Header
        .param("key", "value")                             // Query 參數
        .cookie(new Cookie("session", "abc123"))           // Cookie
        .sessionAttr("userId", 123L)                       // Session 屬性
        .with(csrf())                                      // CSRF Token (Spring Security)
        .with(user("admin").roles("ADMIN"))                // 模擬認證用戶
).andExpect(status().isOk())
 .andExpect(jsonPath("$.id").exists())
 .andExpect(jsonPath("$.name").value("張三"))
 .andDo(print()); // 列印完整請求/回應,調試神器

核心斷言匹配器

分類匹配器說明
狀態status().isOk()200
status().isCreated()201
status().isBadRequest()400
status().isNotFound()404
內容content().string("text")完整字串匹配
content().json("{}")JSON 結構匹配
content().contentType(MediaType.APPLICATION_JSON)Content-Type
content().contentTypeCompatibleWith(...)兼容類型
content().encoding("UTF-8")編碼
JSON PathjsonPath("$.id").exists()節點存在
jsonPath("$.name").value("張三")值精確匹配
jsonPath("$.age").value(greaterThan(18))Hamcrest 匹配器
jsonPath("$.items[*].id").value(hasSize(3))陣列大小
jsonPath("$..name").value(contains("張三"))遞歸搜尋包含
jsonPath("$.timestamp").value(matchesPattern("\\d{13}"))正則匹配
Headerheader().string("Location", "/users/1")Header 值
header().exists("ETag")Header 存在
header().stringValues("Set-Cookie", contains("session="))多值 Header
header().longValue("Content-Length", greaterThan(0L))數值 Header
Cookiecookie().value("JSESSIONID", notNullValue())Cookie 值
cookie().maxAge("remember-me", greaterThan(0))Max-Age
cookie().httpOnly("auth", true)HttpOnly 標記

完整測試類範例

@WebMvcTest(UserController.class) // 只載入 Web 層,快速啟動
@AutoConfigureMockMvc
class UserControllerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @MockBean
    private UserService userService;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @Test
    @DisplayName("建立用戶 - 成功")
    void createUser_success() throws Exception {
        // Given
        UserDTO dto = new UserDTO("張三", "zhangsan@example.com");
        UserVO vo = new UserVO(1L, "張三", "zhangsan@example.com");
        when(userService.create(any())).thenReturn(vo);
        
        // When & Then
        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(dto)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.id").value(1))
            .andExpect(jsonPath("$.name").value("張三"))
            .andExpect(jsonPath("$.email").value("zhangsan@example.com"))
            .andDo(print());
    }
    
    @Test
    @DisplayName("查詢用戶 - 不存在返回 404")
    void getUser_notFound() throws Exception {
        when(userService.getById(999L)).thenThrow(new UserNotFoundException(999L));
        
        mockMvc.perform(get("/api/users/999"))
            .andExpect(status().isNotFound())
            .andExpect(jsonPath("$.code").value("USER_NOT_FOUND"))
            .andExpect(jsonPath("$.message").value("用戶不存在: 999"));
    }
}

進階技巧

1. 自定義 ResultMatcher 封裝業務斷言


2. 測試異步 Controller (DeferredResult/Callable)

@WithMockUser(username = "admin", roles = "ADMIN") // 方法級
// 或
.with(user("admin").roles("ADMIN")) // 請求級

4. 獨立設置模式 (非 @WebMvcTest)


常見問題排查

問題原因解決
406 Not AcceptableAccept 標頭不匹配 produces檢查 @RequestMapping(produces) 或請求 .accept()
415 Unsupported Media TypeContent-Type 不匹配 consumes請求 .contentType(MediaType.APPLICATION_JSON)
JSON 斷言失敗字段名/類型不匹配用 .andDo(print()) 列印實際回應對比
MockBean 不生效未掃描到配置類確認 @SpringBootTest/@WebMvcTest 掃描路徑

總結

  • 靜態導入 MockMvcRequestBuilders.*、MockMvcResultMatchers.*、MockMvcResultHandlers.* 是標準配置
  • 鏈式 API 讓測試代碼如文檔般清晰:perform → andExpect → andDo
  • 善用 jsonPath + Hamcrest 匹配器進行精準 JSON 驗證
  • .andDo(print()) 是調試利器,CI 失效時首選查看輸出
  • 優先使用 @WebMvcTest 切片測試,啟動快、聚焦 Web 層

掌握 MockMvc 靜態導入與流暢 API,能讓 Controller 測試從「寫起來痛」變成「讀起來爽」,大幅提升測試覆蓋率與維護性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved