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

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

Joe Tsai
Joe Tsai
Sep 19, 2018 · 5 min read

前情提要

在 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:

Joe Blog

想到什麼寫什麼

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store