接下來的測試將會需要用到 mocking 的 library ,在 Android 大家比較常用的是 MockK 和 Mockito 。這篇要介紹的是 MockK,它是一個專門支援 Kotlin 的 mocking library ,來看看它有哪些特點吧!
如果沒有寫測試經驗的朋友應該不太了解甚麼是 mocking ,我們可以用一個例子來了解一下。假如我們有個 ApiService ,它對外提供 User 實體給其他人,而 ApiService 又實作了 Service 這個介面。
data class User(val name: String, val id: Long)
interface Service {
fun getUser(): User
}
class ApiService : Service {
override fun getUser() = User("John", 1L)
}
有另一個類別 UserRepository 用到了 Service 介面來提供 User 。
class UserRepository(private val service: Service) {
fun getUser(): Flow<User> = flow {
if (user.id == -1) {
throw new InvalidUserIdException()
} else {
emit(service.getUser())
}
}
}
這時候,我們要針對 UserRepository 來寫測試的話,首先第一個要做的事情就是控制外部來的變數,也就是 Service ,因為他會控制我們 User 實體。如果我想要 UserRepository 丟出 InvalidUserIdException 錯誤的話,我可以透過一個假的 service 發出一個 id 為 -1 的假 User 來測試程式是不是會判斷 id 為 -1 。相反地,我們也可以測試正向的流程,也就是 id 不為 -1 的流程,這個就是 mocking 可以做的事情。
以上面這個例子如果使用 MockK 可以怎麼測?
@Test
fun `Test getting an user info by MockK`() = runBlocking {
val fakeUser = User("Doe", 2L)
val mockApiService = mockk<ApiService>(relaxed = true)
every { mockApiService.getUser() } returns fakeUser
val userRepository = UserRepository(mockApiService)
userRepository.getUser().collect {
assertEquals(it, fakeUser)
}
}
做一個 fakeUser 放到假的 ApiService 裡面,在 MockK 要做一個假的物件只要用 mockk<ApiService> 就可以了!下一行的 every...return 表示法就是指定當每次 mockApiService.getUser() 被呼叫的時候要回甚麼指定物件給呼叫的人,這邊我們指定 fakeUser 。另外,你可能也有發現 mockk<ApiService> 後面有個 relaxed = true 的參數,這個主要的是讓 mocking 的物件內的東西被呼叫的時候,有回給他一個基本值或是不作用,這個的好處是,假設今天我們的 ApiService 有 10 個方法,但我們只要操控 getUser 的結果,另外 9 個不管,我們只要對 getUser 做 every...return 塞值,另外 9 個不用一個個塞值。
另一個測試路線就是測試當 id 是 -1 的時候會不會丟 exception ,可以在測試方法前面寫上 @Test(expected = InvalidUserIdException::class) ,意思就是說我們期待這邊會丟出 InvalidUserIdException 。
@Test(expected = InvalidUserIdException::class)
fun `Test getting an user info by MockK`() = runBlocking {
val fakeUser = User("Doe", -1L)
val mockApiService = mockk<ApiService>(relaxed = true)
every { mockApiService.getUser() } returns fakeUser
val userRepository = UserRepository(mockApiService)
userRepository.getUser().collect()
}
MockK 在測試 Kotlin 程式碼的時候真的很好用,因為他的 API 是支援 Kotlin 的寫法,而且還有類似 DSL 的表達方式,讓我們在寫測試的時候可以很輕鬆地完成要寫的測試。如果你對於 MockK 有興趣的話,可以參考一下官網文件,他們的文件也是簡單易懂,讓測試可以簡單就上手。