Upgrade to Pro — share decks privately, control downloads, hide ads and more …

MockK: mocking library for Kotlin

Joe Tsai
September 20, 2018

MockK: mocking library for Kotlin

詳細文章介紹:https://bit.ly/2PP6gJ8

Joe Tsai

September 20, 2018
Tweet

More Decks by Joe Tsai

Other Decks in Programming

Transcript

  1. 嘗試 @mock Class org.mockito.exceptions.base.MockitoException: Cannot mock/spy class com.joetsai.kotlinunittest.token.TokenRepository Mockito cannot

    mock/spy because : - final class → 在 Kotlin 中任何 Class 預設為 final 解法 1. Open class 2. 開啟隱藏版 mock final 功能
  2. 其他可能遇到的問題 • java.lang.IllegalStateException: anyObject() must not be null any(), eq(),

    capture(), captor() • `when` 解法 1. MockitoKotlinHelpers.kt 2. Mockito-Kotlin
  3. Example: 小孩要零用錢 class Kid(private val mother: Mother) { var money

    = 0 private set fun wantMoney() { money += mother.giveMoney() } } class Mother { fun giveMoney(): Int { return 100 } }
  4. mockk, every class KidTest { @Test fun wantMoney() { //

    Given val mother = mockk<Mother>() val kid = Kid(mother) every { mother.giveMoney() } returns 30 // when().thenReturn() in Mockito // When kid.wantMoney() // Then assertEquals(30, kid.money) } }
  5. Annotation class KidAnnotationTest { @MockK lateinit var mother: Mother lateinit

    var kid: Kid @Before fun setUp() { MockKAnnotations.init(this) kid = Kid(mother) } @Test fun wantMoney() { every { mother.giveMoney() } returns 30 kid.wantMoney() assertEquals(30, kid.money) } }
  6. 需求變更:通知媽媽 class Kid(private val mother: Mother) { var money =

    0 private set fun wantMoney() { mother.inform(money) money += mother.giveMoney() } } class Mother { fun inform(money: Int) { println("媽媽我現在有 $money 元,我要跟你拿錢!") } fun giveMoney(): Int { return 100 } }
  7. Verify @Test fun wantMoney() { // When val mother =

    mockk<Mother>() val kid = Kid(mother) every { mother.giveMoney() } returns 30 // Given kid.wantMoney() // Then verify { mother.inform(any()) } assertEquals(30, kid.money) }
  8. Verify @Test fun wantMoney() { // When val mother =

    mockk<Mother>() val kid = Kid(mother) every { mother.giveMoney() } returns 30 // Given kid.wantMoney() // Then verify { mother.inform(any()) } assertEquals(30, kid.money) } io.mockk.MockKException: no answer found for: Mother(#1).inform(0)
  9. Verify @Test fun wantMoney() { // When val mother =

    mockk<Mother>() val kid = Kid(mother) every { mother.giveMoney() } returns 30 // Given kid.wantMoney() // Then verify { mother.inform(any()) } assertEquals(30, kid.money) } io.mockk.MockKException: no answer found for: Mother(#1).inform(0) 在 MockK 預設的 mock 是很嚴謹的,必須指定所 有的行為操作。
  10. Verify @Test fun wantMoney() { // When val mother =

    mockk<Mother>() val kid = Kid(mother) every { mother.giveMoney() } returns 30 // Given kid.wantMoney() // Then verify { mother.inform(any()) } assertEquals(30, kid.money) } io.mockk.MockKException: no answer found for: Mother(#1).inform(0) 在 MockK 預設的 mock 是很嚴謹的,必須指定所 有的行為操作。 every { mother.inform(any()) } just Runs
  11. Relaxed Unit Function 不需指定無回傳值的方法 @MockK lateinit var mother: Mother @Before

    fun setUp() { MockKAnnotations.init(this, relaxUnitFun = true) } @MockK(relaxUnitFun = true) lateinit var mother: Mother
  12. Verify verify { mother.inform(any()) } verify(exactly = 0) { mother.inform(any())

    } verify { mother.inform(any()) mother.giveMoney() } // inform() 的下一個執行的方法一定要是 giveMoney() verifySequence { mother.inform(any()) mother.giveMoney() } // inform() 只要在 giveMoney() 之前執行即可 verifyOrder { mother.inform(any()) mother.giveMoney() }
  13. Capture 用途:抓取方法參數 種類:slot, mutableListOf class Mother { fun inform(money: Int)

    { println("媽媽我現在有 $money 元,我要跟你拿錢!") } ... } val slot = slot<Int>() every { mother.inform(capture(slot)) } just Runs kid.wantMoney() assertEquals(0, slot.captured)
  14. Static Method class Util { fun ok() { UtilJava.ok() UtilKotlin.ok()

    } } object UtilKotlin { @JvmStatic fun ok(): String { return "UtilKotlin.ok()" } } public class UtilJava { static String ok() { return "UtilJava.ok()"; } }
  15. @Test fun ok() { // Given val util = Util()

    mockkStatic(UtilJava::class) mockkStatic(UtilKotlin::class) every { UtilJava.ok() } returns "Joe" every { UtilKotlin.ok() } returns "Tsai" // When util.ok() // Then verify { UtilJava.ok() } verify { UtilKotlin.ok() } assertEquals("Joe", UtilJava.ok()) assertEquals("Tsai", UtilKotlin.ok()) } MockkStatic
  16. Companion Object ? class Util { fun ok() { UtilKotlin.ok()

    } } class UtilKotlin { companion object { @JvmStatic fun ok(): String { return "UtilKotlin.ok()" } } }
  17. Test Companion Object @Test fun ok() { // Given val

    util = Util() mockkObject(UtilKotlin) mockkObject(UtilKotlin.Companion) every { UtilKotlin.ok() } returns "Tsai" // When util.ok() // Then verify { UtilKotlin.ok() } assertEquals("Tsai", UtilKotlin.ok()) } 兩個都能正常運作
  18. class Util { fun ok() { UtilKotlin.ok() } } Singleton

    object UtilKotlin { @JvmStatic fun ok(): String { return "UtilKotlin.ok()" } }
  19. MockkObject @Test fun ok() { // Given val util =

    Util() mockkObject(UtilKotlin) every { UtilKotlin.ok() } returns "Tsai" // When util.ok() // Then verify { UtilKotlin.ok() } assertEquals("Tsai", UtilKotlin.ok()) }