前情提要
在 Java 的世界裡面,說到單元測試,不得不提到 Mockito,它是目前 Java 最多人使用的 Mocking framework,但若來到 Kotlin 的世界呢?我們還能仰賴 Mockito 進行單元測試嗎?有沒有專門為 Kotlin 這款語言量身打造的 Mocking framework 呢?
這系列文章會手把手教你如何使用 MockK,讓你了解它好用的地方在哪裡?以及它解決了什麼樣的問題?一共分成 4 篇文章,包括以下內容:
- 用 Kotlin Mockito 寫單元測試會碰到什麼問題?
- MockK 功能介紹:mockk, every, Annotation, verify
- MockK 功能介紹:Relaxed Mocks, 再談 Verify , Capture(這篇)
- 如何測試 Static Method, Singleton
Relaxed Mocks
【接續前文⋯⋯】
上面這種做法隱藏著一個問題:「假設今天 Class 的方法有 100 個,那豈不是要指定到天荒地老了嗎?」有沒有辦法跟 Mockito 一樣不用指定行為也能做後續的
verify
?
這時候你要用的就是 Relaxed Mocks
,依其功能性分成 Relaxed 跟 RelaxUnitFun 兩種模式:
Relaxed
用途:該物件所有的方法都不需要指定
只要在 Mock 物件時,後面加上 relaxed = true
即可:
val mother = mockk<Mother>(relaxed = true)
也可用 Annotation 的方式宣告,不過要改用 @RelaxedMockK
:
@RelaxedMockK
lateinit var mother: Mother
或者全域性設定所有的 @MockK
成 relaxed
:
@MockK
lateinit var mother: Mother@Before
fun setUp() {
MockKAnnotations.init(this, relaxed = true)
}
RelaxUnitFun
用途:不需指定無回傳值的方法,有回傳值的方法仍須指定
第一種是在 Annotation 後面加上 relaxUnitFun = true
:
@MockK(relaxUnitFun = true)
lateinit var mother: Mother
第二種是全域性的設定,將所有的 @MockK
變成 relaxUnitFun
:
@MockK
lateinit var mother: Mother@Before
fun setUp() {
MockKAnnotations.init(this, relaxUnitFun = true)
}
再談 Verify
接下來介紹 Verify 的進階用法,剛剛我們用到的是 Verify 最基本的用法:
verify { mother.inform(any()) }
也就是 mother.inform()
這個方法會被呼叫一次,如果你想要指定方法被呼叫的次數可以在前面加上 exactly
:
verify(exactly = 0) { mother.inform(any()) }
verify(exactly = 10) { mother.inform(any()) }
exactly = 0
代表這段方法不會被呼叫到,exactly = 10
代表會被呼叫 10 次,其他以此類推⋯⋯
我們也可以在 verify
的 lambda 裡面放入多個方法,進行驗證:
verify {
mother.inform(any())
mother.giveMoney()
}
這代表 mother.inform()
跟 mother.giveMoney()
都會被呼叫到一次。
想要驗證方法被呼叫的順序性可以使用 verifySequence
跟 verifyOrder
:
verifySequence {
mother.inform(any())
mother.giveMoney()
}verifyOrder {
mother.inform(any())
mother.giveMoney()
}
verifySequence
規定 inform()
的下一個執行的方法一定要是 giveMoney()
,否則測試失敗。
verifyOrder
條件比較寬鬆,inform()
只要在 giveMoney()
之前執行即可,功能跟 Mockito 的 inOrder 一樣。
Capture
用途:要抓取方法的參數值時
種類:slot
用來抓取一個值,mutableListOf
用來抓取一段連續資料(List)
依舊是同個範例:
我們來抓取 inform()
這個方法傳入的 money 參數:
// Given
val slot = slot<Int>()
every { mother.inform(capture(slot)) } just Runs// When
kid.wantMoney()// Then
assertEquals(0, slot.captured)
money 是一個 Int,所以我們宣告一個 slot
,並在 every
裡面呼叫 capture()
進行抓取,要使用這個值的時候再呼叫 slot.captured
即可。
這篇文章我們介紹了 Relaxed Mocks
、verify
以及 Capture
的用法。下篇文章將會介紹如何測試 Static Method 及 Singleton: