此篇想談論單元測試並使用 Junit
工具進行測試撰寫,單元測試是針對程式模組(軟體設計的最小單位)進行正確性檢驗的測試工作,並且是一段可自動化執行的程式碼,程式會呼叫被測試的工作單元,再針對此單元所執行的最終結果進行假設驗證,驗證此單元結果是否符合我們所預期的行為,而工作單元通常是程式模組最小的單位,當單元測試檢測發現程式錯誤時,我們也可以在第一時間進行修正,已證實程式達到專案需求目標,故單元測試應該具備以下特質:
好的單元測試,應該要具備三種特色:
可信賴性(Trustworthiness)
開發者應對自己所撰寫測試的結果有信心,並且是針對實際專案需求進行正確的測試
可維護性(Maintainability)
測試也應保持好的可維護性,無法維護的測試會是一場惡夢,只會導致拖累專案整體進度
可閱讀性(Readability)
每次修改程式時都會持續進行單元測試檢測,當測試發生問題時,為了快速找到癥結點所在,保持好的閱讀性相當重要。
而實際在測試方法撰寫中,我們可以採取 3A
測試原則,如下:
Arrange
初始化目標物件、相依物件、方法參數、預期結果Act
執行測試工作單元,取得實際測試結果Assert
驗證結果是否符合預期結果以下直接將先前的 RESTful API 範例撰寫 Service Unit Test:
Spring Boot 在建置專案時已經先引入 Test 套件org.springframework.boot:spring-boot-starter-test,裡面會包含相關測試模組,如 Junit、AssertJ、Mockito等元件
測試類別設定參數(@SpringBootTest、@MockBean、@Autowired):
@SpringBootTest Annotation 會為我們引入測試元件
@MockBean 則是要新增一個 DAO 假物件,幫助我們順利進行Service的單元測試
@Autowired 新增一個 Service 物件進行測試
@SpringBootTest
class TestStudentService {
@MockBean
lateinit var studentDao: StudentDao
@Autowired
lateinit var studentServiceImpl: StudentServiceImpl
}
加入測試方法
測試取得所有學生資料
@Test
fun shouldGetAllStudentWhenCallMethod() {
// Arrange 初始化測試資料與預期結果
val expectedResult : MutableList<Student> = mutableListOf<Student>()
expectedResult.add(Student(1, "Devin", "devin@gmail.com"))
expectedResult.add(Student(2, "Eric", "eric@gmail.com"))
given(studentDao.findAll()).willReturn(expectedResult)
// Act 執行測試工作單元,取得實際測試結果
val actual : MutableList<Student> = studentServiceImpl.findAllStudent()
// Assert 驗證結果是否符合預期結果
assertEquals(expectedResult, actual)
}
測試利用 id 取得單一學生資料
@Test
fun shouldGetOneStudentWhenCallMethodById() {
val expectedResult = Student(1, "Devin", "devin@gmail.com")
given(studentDao.findById(1)).willReturn(expectedResult)
val actual : Student? = studentServiceImpl.findByStudentId(1)
assertEquals(expectedResult, actual)
}
測試利用 Name 欄位取得學生資料
@Test
fun shouldGetStudentsWhenCallMethodByName() {
val expectedResult : MutableList<Student> = mutableListOf<Student>()
expectedResult.add(Student(1, "Devin", "devin@gmail.com"))
given(studentDao.findByName("Devin")).willReturn(expectedResult)
val actual : MutableList<Student> = studentServiceImpl.findByStudentName("Devin")
assertEquals(expectedResult, actual)
}
測試建立學生資料
@Test
fun shouldGetNewStudentWhenCallMethodByStudent() {
val expectedResult = Student( 1, "Devin", "devin@gmail.com")
val requestParameter = Student( name = "Devin", email = "devin@gmail.com")
given(studentDao.save(requestParameter)).willReturn(expectedResult)
val actual : Student = studentServiceImpl.addStudent(requestParameter)
assertEquals(expectedResult, actual)
}
測試更新整個學生資料
@Test
fun shouldUpdatedStudentWhenCallMethodByStudent() {
val expectedResult = Student(1, "Devin", "devin@gmail.com")
val requestParameter = Student(1, "Eric", "eric@gmail.com")
given(studentDao.save(requestParameter)).willReturn(expectedResult)
val actual : Student? = studentServiceImpl.updateStudent(requestParameter)
assertEquals(expectedResult, actual)
}
測試更新學生信箱
@Test
fun shouldUpdatedEmailWhenCallMethodByStudent() {
val expectedResult = Student(1, "Devin", "devin@gmail.com")
val requestParameter = Student(1, "Devin", "test@gmail.com")
given(studentDao.save(requestParameter)).willReturn(expectedResult)
val actual : Student? = studentServiceImpl.updateStudentEmail(requestParameter)
assertEquals(expectedResult.email, actual?.email)
}
測試刪除學生資料
@Test
fun shouldDeletedStudentWhenCallMethodByStudent() {
val expectedResult = true
val expectedSaveResult = Student(1, "Devin", "devin@gmail.com")
given(studentDao.findById(1)).willReturn(expectedSaveResult)
val actual = studentServiceImpl.deleteStudent(1)
assertEquals(expectedResult, actual)
}
此文章有提供範例程式碼在 Github 供大家參考