MockK:一款強大的 Kotlin Mocking Library (Part 3 / 4)

MockK 功能介紹:Relaxed Mocks, 再談 Verify, Capture

前情提要

在 Java 的世界裡面,說到單元測試,不得不提到 Mockito,它是目前 Java 最多人使用的 Mocking framework,但若來到 Kotlin 的世界呢?我們還能仰賴 Mockito 進行單元測試嗎?有沒有專門為 Kotlin 這款語言量身打造的 Mocking framework 呢?

這系列文章會手把手教你如何使用 MockK,讓你了解它好用的地方在哪裡?以及它解決了什麼樣的問題?一共分成 4 篇文章,包括以下內容:

  1. 用 Kotlin + Mockito 寫單元測試會碰到什麼問題?
  2. MockK 功能介紹:mockk, every, Annotation, verify
  3. MockK 功能介紹:Relaxed Mocks, 再談 Verify , Capture(這篇)
  4. 如何測試 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

或者全域性設定所有的 @MockKrelaxed

@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() 都會被呼叫到一次。

想要驗證方法被呼叫的順序性可以使用 verifySequenceverifyOrder

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 Mocksverify 以及 Capture 的用法。下篇文章將會介紹如何測試 Static Method 及 Singleton: