上一篇我們完成了 Service 的單元測試,而這篇我們要來測試 Controller
單元測試,在前面架構章節有提到 Controller 是負責處理 Http 請求、路由處理、身份驗證與Json資料轉換等處理內容,而在測試我們為了要處理這些內容,會需要使用到兩個新的測試物件,@WebMvcTest
負責處理 Http請求與路由處理,ObjectMapper
負責協助我們將物件轉換為 Json 資料,以下直接進入實作觀察:
在測試Test資料夾新增測試檔案-TestStudentController
,並替測試類別設定參數(@WebMvcTest
、@MockBean
、@Autowired
、ObjectMapper
),範例如下:
@WebMvcTest(StudentController::class)
class TestStudentController() {
@MockBean
lateinit var studentServiceImpl: StudentServiceImpl
@Autowired
lateinit var mockMvc: MockMvc
private val objectMapper = ObjectMapper()
}
加入測試方法
取得所有學生資料 API
@Test
fun shouldGetAllStudentWhenCallMethod() {
val expectedResult : MutableList<Student> = mutableListOf<Student>()
expectedResult.add(Student(1, "Devin", "devin@gmail.com"))
expectedResult.add(Student(2, "Eric", "eric@gmail.com"))
given(studentServiceImpl.findAllStudent()).willReturn(expectedResult)
mockMvc.perform(
get("/api/students")
.contentType(MediaType.APPLICATION_JSON)
).andExpect(status().isOk)
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string(objectMapper.writeValueAsString(expectedResult)))
}
取得單一學生資料 API
@Test
fun shouldGetOneStudentWhenCallMethodById() {
val expectedResult : MutableList<Student> = mutableListOf<Student>()
expectedResult.add(Student(1, "Devin", "devin@gmail.com"))
given(studentServiceImpl.findByStudentName("Devin")).willReturn(expectedResult)
mockMvc.perform(
post("/api/students/search")
.contentType(MediaType.APPLICATION_JSON)
.param("name", "Devin")
).andExpect(status().isOk)
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string(objectMapper.writeValueAsString(expectedResult)))
}
建立學生資料 API
@Test
fun shouldGetNewStudentWhenCallMethodByStudent() {
val expectedResult = Student( 1, "Devin", "devin@gmail.com")
val requestParameter = Student( name = "Devin", email = "devin@gmail.com")
given(studentServiceImpl.addStudent(requestParameter)).willReturn(expectedResult)
mockMvc.perform(
post("/api/students")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(requestParameter))
).andExpect(status().isOk)
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string(objectMapper.writeValueAsString(expectedResult)))
}
更新學生資料 API
@Test
fun shouldUpdatedStudentWhenCallMethodByStudent() {
val expectedResult = Student(1, "Devin", "devin@gmail.com")
val requestParameter = Student(1, "Eric", "eric@gmail.com")
given(studentServiceImpl.findByStudentId(1)).willReturn(requestParameter)
given(studentServiceImpl.updateStudent(requestParameter)).willReturn(expectedResult)
mockMvc.perform(
put("/api/students/1")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(requestParameter))
).andExpect(status().isOk)
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string(objectMapper.writeValueAsString(expectedResult)))
}
更新學生信箱資料 API
@Test
fun shouldUpdatedEmailWhenCallMethodByStudent() {
val expectedResult = Student(1, "Devin", "devin@gmail.com")
val requestParameter = Student(1, "Devin", "test@gmail.com")
given(studentServiceImpl.findByStudentId(1)).willReturn(requestParameter given(studentServiceImpl.updateStudentEmail(requestParameter)).willReturn(expectedResult)
mockMvc.perform(
patch("/api/students/1")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(requestParameter))
).andExpect(status().isOk)
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string(objectMapper.writeValueAsString(expectedResult)))
}
刪除學生資料成功 API
@Test
fun shouldGetIsNotContentStatusWhenDeleteSuccess() {
val expectedResult = true
given(studentServiceImpl.deleteStudent(1)).willReturn(expectedResult)
mockMvc.perform(
delete("/api/students/1")
.contentType(MediaType.APPLICATION_JSON)
).andExpect(status().isNoContent)
}
刪除學生資料失敗 API
@Test
fun shouldGetBadRequestStatusWhenDeleteFailed() {
val expectedResult = false
given(studentServiceImpl.deleteStudent(1)).willReturn(expectedResult)
mockMvc.perform(
delete("/api/students/1")
.contentType(MediaType.APPLICATION_JSON)
).andExpect(status().isBadRequest)
}
最後我們利用下圖來觀察專案的測試覆蓋率(Test Coverage)發現都是 100%,表示我們已經將每一段程式都有進行測試,但這邊建議測試覆蓋率只是一個參考數值,頂多只能了解自己有沒有地方少做測試,這與專案會不會有問題沒有絕對關係,以前有待過專案測試覆蓋率要求100%的團隊,也有待過測試覆蓋率要求 70-80% 左右的團隊,但個人覺得最主要還是在於我們是否有將產品核心相關的功能盡可能做到測試,畢竟這些功能才是與使用者有高度相關的。
此文章有提供範例程式碼在 Github 供大家參考