在上一篇文章中,我們已經測試了 Service
的部分
今天要來看 Controller
的部分,探討如何進行測試
在這裡我們就不用和 Service
一樣的單元測試的方式
我們會使用 Spring 框架提供給我們的 MockMVC
這個工具,它專門用來測試 Spring MVC 應用程式的控制器(Controller)
它允許開發者模擬 HTTP 的動作,而無需實際啟動伺服器
HTTP 方法
(GET、POST、PUT、DELETE 等)的處理HTTP 狀態碼
符合預期
各種輸入
正常情況
和錯誤處理
Mockito mock 物件
到 Spring 應用程式中已存在的 bean
TodoService
,讓我們能夠控制服務層的行為一樣先在 test
的 package 裡面,建立相對應的 controller package,然後建立一個 TodoControllerTest
的測試類別
下面我們測試了主要的 CRUD 相關功能
@WebMvcTest(TodoController.class)
public class TodoControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private TodoService todoService;
@Autowired
private ObjectMapper objectMapper;
@Test
public void createTodo() throws Exception {
Todo todo = new Todo(null, "新待辦事項", false);
Todo savedTodo = new Todo(1L, "新待辦事項", false);
when(todoService.save(any(Todo.class))).thenReturn(savedTodo);
mockMvc.perform(post("/api/todos")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(todo)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.id").value(1))
.andExpect(jsonPath("$.data.title").value("新待辦事項"))
.andExpect(jsonPath("$.data.completed").value(false));
verify(todoService, times(1)).save(any(Todo.class));
}
@Test
public void getAllTodos() throws Exception {
Todo todo1 = new Todo(1L, "測試待辦事項", false);
Todo todo2 = new Todo(2L, "測試待辦事項2", true);
when(todoService.findAll()).thenReturn(Arrays.asList(todo1, todo2));
mockMvc.perform(get("/api/todos")).andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data[0].id").value(1))
.andExpect(jsonPath("$.data[0].title").value("測試待辦事項"))
.andExpect(jsonPath("$.data[0].completed").value(false))
.andExpect(jsonPath("$.data[1].id").value(2))
.andExpect(jsonPath("$.data[1].title").value("測試待辦事項2"))
.andExpect(jsonPath("$.data[1].completed").value(true));
verify(todoService, times(1)).findAll();
}
@Test
public void getTodo() throws Exception {
Todo todo = new Todo(1L, "測試待辦事項", false);
when(todoService.findById(1L)).thenReturn(Optional.of(todo));
mockMvc.perform(get("/api/todos/1")).andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.id").value(1))
.andExpect(jsonPath("$.data.title").value("測試待辦事項"))
.andExpect(jsonPath("$.data.completed").value(false));
verify(todoService, times(1)).findById(1L);
}
@Test
public void updateTodo() throws Exception {
Todo updatedTodo = new Todo(1L, "更新的待辦事項", true);
when(todoService.updateTodo(eq(1L), any(Todo.class)))
.thenReturn(Optional.of(updatedTodo));
mockMvc.perform(put("/api/todos/1")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(updatedTodo)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.id").value(1))
.andExpect(jsonPath("$.data.title").value("更新的待辦事項"))
.andExpect(jsonPath("$.data.completed").value(true));
verify(todoService, times(1)).updateTodo(eq(1L), any(Todo.class));
}
@Test
public void deleteTodo() throws Exception {
when(todoService.deleteTodo(1L)).thenReturn(true);
mockMvc.perform(delete("/api/todos/1")).andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true));
verify(todoService, times(1)).deleteTodo(1L);
}
}
要注意 import 的 package
- MockServer 開頭的,主要用於 WebFlux
- MockMvc 開頭的,主要用於 Spring MVC - 這才是我們要 import 的
在測試中,@WebMvcTest(TodoController.class)
告訴 Spring Boot 只初始化與 TodoController
相關的 Spring MVC 基礎設施
每個測試方法都使用 MockMvc
來模擬 HTTP 請求,並使用 Mockito
來模擬 TodoService
的行為
我們驗證了回應的狀態碼
和內容
,確保控制器正確處理請求並返回預期的結果
已經寫了一篇文章來介紹三種優雅的 JSON 驗證方法,有興趣的可以看看:Day 32.5 進化你的Spring Boot測試:三種優雅的JSON驗證方法
MockMVC
測試是 Spring Boot 應用程式中不可或缺的一部分
它們提供了一種有效的方式來測試控制器層的邏輯,而無需啟動整個應用程式
在實際開發中,建議為每個控制器方法編寫全面的測試案例,包括正向和反向的情境
這樣可以大大提高應用程式的品質,並在進行修改或重構時提供安全網
我的粉絲專頁
圖片來源:AI 產生