iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 23
0
Modern Web

30天從零撰寫 Kotlin 語言並應用於 Spring Boot 開發系列 第 23

[Day 23] 遠征 Kotlin × Spring Boot 介紹單元測試 (2)

上一篇我們完成了 Service 的單元測試,而這篇我們要來測試 Controller 單元測試,在前面架構章節有提到 Controller 是負責處理 Http 請求、路由處理、身份驗證與Json資料轉換等處理內容,而在測試我們為了要處理這些內容,會需要使用到兩個新的測試物件,@WebMvcTest 負責處理 Http請求與路由處理,ObjectMapper 負責協助我們將物件轉換為 Json 資料,以下直接進入實作觀察:

  1. 在測試Test資料夾新增測試檔案-TestStudentController,並替測試類別設定參數(@WebMvcTest@MockBean@AutowiredObjectMapper),範例如下:

    @WebMvcTest(StudentController::class)
    class TestStudentController() {
    
        @MockBean
        lateinit var studentServiceImpl: StudentServiceImpl
    
        @Autowired
        lateinit var mockMvc: MockMvc
    
        private val objectMapper = ObjectMapper()
    
    }
    
  2. 加入測試方法

    • 取得所有學生資料 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% 左右的團隊,但個人覺得最主要還是在於我們是否有將產品核心相關的功能盡可能做到測試,畢竟這些功能才是與使用者有高度相關的。

https://ithelp.ithome.com.tw/upload/images/20201002/20121179khZSQzYBOb.png

此文章有提供範例程式碼在 Github 供大家參考


上一篇
[Day 22] 遠征 Kotlin × Spring Boot 介紹單元測試 (1)
下一篇
[Day 24] 遠征 Kotlin × Spring Boot 介紹 Template Engine (1)
系列文
30天從零撰寫 Kotlin 語言並應用於 Spring Boot 開發30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言