iT邦幫忙

2021 iThome 鐵人賽

DAY 24
0
Mobile Development

如何使用 Kotlin Annotation Processor 做出自己的 Custom Data Parser Library系列 第 24

使用 MockK 做測試

接下來的測試將會需要用到 mocking 的 library ,在 Android 大家比較常用的是 MockK 和 Mockito 。這篇要介紹的是 MockK,它是一個專門支援 Kotlin 的 mocking library ,來看看它有哪些特點吧!

  • 簡單易懂的 API ,有類似 DSL 的寫法。
  • 支援多種 Kotlin 寫法的 mocking ,像是 object 、 coroutines extension 、 function 等。
  • 很方便可以驗證一些 custom 的邏輯或是程式呼叫順序。

如果沒有寫測試經驗的朋友應該不太了解甚麼是 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 個不管,我們只要對 getUserevery...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 有興趣的話,可以參考一下官網文件,他們的文件也是簡單易懂,讓測試可以簡單就上手。


上一篇
Parser 的單元測試
下一篇
Reader 的 MockK 測試
系列文
如何使用 Kotlin Annotation Processor 做出自己的 Custom Data Parser Library30

尚未有邦友留言

立即登入留言