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

Mocking Kotlin with MockK

Hadi Tok
September 20, 2019

Mocking Kotlin with MockK

Introduction to the mocking library MockK

Hadi Tok

September 20, 2019
Tweet

More Decks by Hadi Tok

Other Decks in Programming

Transcript

  1. Mocking Kotlin With MockK Hadi Tok CitizenMe @hadi_tok

  2. Unit Testing

  3. Unit Testing • Unit: Smallest testable component (function, class) •

    Units are tested independently from each other • Other units with dependencies are mocked or faked
  4. Unit Testing

  5. Testable Code • Separations of Concerns • Dependency injection •

    Clean Code
  6. Stub • Returning values without logic. • Could return constant

    values or use a lookup table
  7. Fake • Could have lightweight logic • Cut corners for

    testing purposes so can’t be used in production
  8. Mock Northern mockingbird (Mimus polyglottos)

  9. Mock • Not authentic or real, but without the intention

    to deceive. • Imitates objects to specify behaviour explicitly and enables full control over the object. • Usually backed with a library/framework
  10. Mocking • What functions and variables return • Check if

    the function calls and variable assignments are made with correct values
  11. MockK

  12. Why MockK • Pure Kotlin • Easy to use Domain

    Specific Language(DSL) • Can also mock Java
  13. Sample

  14. data class User(val userId: String, val userName: String)

  15. data class User(val userId: String, val userName: String) interface UserApi

    { fun deleteUser(userId: String): Boolean }
  16. data class User(val userId: String, val userName: String) interface UserApi

    { fun deleteUser(userId: String): Boolean } class UserManager(private val userApi: UserApi) { fun deleteUser(user: User): Boolean { return userApi.deleteUser(user.userId) } }
  17. @Test fun basicTest() { val userApi: UserApi = mockk() }

  18. @Test fun basicTest() { val userApi: UserApi = mockk() val

    userManager = UserManager(userApi) }
  19. @Test fun basicTest() { val userApi: UserApi = mockk() val

    userManager = UserManager(userApi) val userId = "123" val user: User = mockk() every { user.userId } returns userId }
  20. @Test fun basicTest() { val userApi: UserApi = mockk() val

    userManager = UserManager(userApi) val userId = "123" val user: User = mockk() every { user.userId } returns userId every { userApi.deleteUser(userId) } returns true }
  21. @Test fun basicTest() { val userApi: UserApi = mockk() val

    userManager = UserManager(userApi) val userId = "123" val user: User = mockk() every { user.userId } returns userId every { userApi.deleteUser(userId) } returns true val result = userManager.deleteUser(user) }
  22. @Test fun basicTest() { val userApi: UserApi = mockk() val

    userManager = UserManager(userApi) val userId = "123" val user: User = mockk() every { user.userId } returns userId every { userApi.deleteUser(userId) } returns true val result = userManager.deleteUser(user) assert(result).toBe(true) }
  23. @Test fun basicTest() { val userApi: UserApi = mockk() val

    userManager = UserManager(userApi) val userId = "123" val user: User = mockk() every { user.userId } returns userId every { userApi.deleteUser(userId) } returns true val result = userManager.deleteUser(user) assert(result).toBe(true) verify { userApi.deleteUser(userId) } }
  24. Argument Matching

  25. @Test fun basicTest() { val userApi: UserApi = mockk() val

    userManager = UserManager(userApi) val userId = "123" val user: User = mockk() every { user.userId } returns userId every { userApi.deleteUser(userId) } returns true val result = userManager.deleteUser(user) assert(result).toBe(true) verify { userApi.deleteUser(userId) } }
  26. @Test fun basicTest() { val userApi: UserApi = mockk() val

    userManager = UserManager(userApi) val userId = "123" val user: User = mockk() every { user.userId } returns userId every { userApi.deleteUser(userId) } returns true val result = userManager.deleteUser(user) assert(result).toBe(true) verify { userApi.deleteUser(match { it.startsWith("12") }) } }
  27. @Test fun basicTest() { val userApi: UserApi = mockk() val

    userManager = UserManager(userApi) val userId = "123" val user: User = mockk() every { user.userId } returns userId every { userApi.deleteUser(userId) } returns true val result = userManager.deleteUser(user) assert(result).toBe(true) verify { userApi.deleteUser(any()) } }
  28. Matchers • more(), less(), range() • eq(), neq(), refEq(), cmpEq()

    • isNull(), isNull(inverse=true) • match{}, matchNullable{}
  29. Annotations

  30. @MockK lateinit var userApi: UserApi @MockK lateinit var user: User

    val userId = “123" lateinit var userManager:UserManager
  31. @MockK lateinit var userApi: UserApi @MockK lateinit var user: User

    val userId = “123" lateinit var userManager:UserManager @Before fun initTests() { MockKAnnotations.init(this) userManager = UserManager(userApi) every { user.userId } returns userId }
  32. @Test fun basicTest() { every { userApi.deleteUser(userId) } returns true

    val result = userManager.deleteUser(user) assert(result).toBe(true) verify { userApi.deleteUser(userId) } }
  33. Object Mock

  34. object Calculator { fun add(first: Int, second: Int) = first

    + second }
  35. object Calculator { fun add(first: Int, second: Int) = first

    + second } @Test fun objectMock() { val result = Calculator.add(1, 2) assert(result).toBe(3) }
  36. object Calculator { fun add(first: Int, second: Int) = first

    + second } @Test fun objectMock() { val result = Calculator.add(1, 2) assert(result).toBe(3) mockkObject(Calculator) every { Calculator.add(1, 2) } returns 42 }
  37. object Calculator { fun add(first: Int, second: Int) = first

    + second } @Test fun objectMock() { val result = Calculator.add(1, 2) assert(result).toBe(3) mockkObject(Calculator) every { Calculator.add(1, 2) } returns 42 }
  38. object Calculator { fun add(first: Int, second: Int) = first

    + second } @Test fun objectMock() { val result = Calculator.add(1, 2) assert(result).toBe(3) mockkObject(Calculator) every { Calculator.add(1, 2) } returns 41 val mockedResult = Calculator.add(1, 2) assert(mockedResult).toBe(42) }
  39. Static Mock

  40. public class CalculatorStatic { static int add(int first, int second){

    return first+second; } }
  41. @Test fun staticMock() { val result = CalculatorStatic.add(1, 2) assert(result).toBe(3)

    }
  42. @Test fun staticMock() { val result = CalculatorStatic.add(1, 2) assert(result).toBe(3)

    mockkStatic(CalculatorStatic::class) }
  43. @Test fun staticMock() { val result = CalculatorStatic.add(1, 2) assert(result).toBe(3)

    mockkStatic(CalculatorStatic::class) every { CalculatorStatic.add(1, 2) } returns 42 }
  44. @Test fun staticMock() { val result = CalculatorStatic.add(1, 2) assert(result).toBe(3)

    mockkStatic(CalculatorStatic::class) every { CalculatorStatic.add(1, 2) } returns 42 val mockedResult = CalculatorStatic.add(1, 2) }
  45. @Test fun staticMock() { val result = CalculatorStatic.add(1, 2) assert(result).toBe(3)

    mockkStatic(CalculatorStatic::class) every { CalculatorStatic.add(1, 2) } returns 42 val mockedResult = CalculatorStatic.add(1, 2) assert(mockedResult).toBe(42) }
  46. Argument Capturing

  47. class Calculator { fun add(first: Int, second: Int) = first

    + second }
  48. class Calculator { fun add(first: Int, second: Int) = first

    + second } @Test fun argumentCapturing() { val calculator:Calculator = mockk() }
  49. class Calculator { fun add(first: Int, second: Int) = first

    + second } @Test fun argumentCapturing() { val calculator:Calculator = mockk() every { calculator.add(any(), any()) } answers { firstArg<Int>() + secondArg<Int>() } }
  50. class Calculator { fun add(first: Int, second: Int) = first

    + second } @Test fun argumentCapturing() { val calculator:Calculator = mockk() every { calculator.add(any(), any()) } answers { firstArg<Int>() + secondArg<Int>() } val mockedResult = calculator.add(1, 2) assert(mockedResult).toBe(3) }
  51. Coroutines

  52. data class User(val userId: String, val userName: String)

  53. data class User(val userId: String, val userName: String) interface UserApi

    { suspend fun deleteUser(userId: String): Boolean }
  54. data class User(val userId: String, val userName: String) interface UserApi

    { suspend fun deleteUser(userId: String): Boolean } class UserManager(private val userApi: UserApi) { suspend fun deleteUser(user: User): Boolean { return userApi.deleteUser(user.userId) } }
  55. @MockK lateinit var userApi: UserApi @MockK lateinit var user: User

    val userId = “123" lateinit var userManager:UserManager @Before fun initTests() { MockKAnnotations.init(this) userManager = UserManager(userApi) every { user.userId } returns userId }
  56. @Test fun basicTest() = runBlocking { }

  57. @Test fun basicTest() = runBlocking { every { user.userId }

    returns userId }
  58. @Test fun basicTest() = runBlocking { every { user.userId }

    returns userId coEvery { userApi.deleteUser(userId) } returns true }
  59. @Test fun basicTest() = runBlocking { every { user.userId }

    returns userId coEvery { userApi.deleteUser(userId) } returns true val result = userManager.deleteUser(user) }
  60. @Test fun basicTest() = runBlocking { every { user.userId }

    returns userId coEvery { userApi.deleteUser(userId) } returns true val result = userManager.deleteUser(user) assert(result).toBe(true) coVerify { userApi.deleteUser(userId) } }
  61. Other features • Spy • Constructor mock • Extension function

    mock • Enumeration mock
  62. Limitations • final static • inline functions

  63. Resources • https://mockk.io/ • Mocking is not rocket science: Basics:

    https://blog.kotlin-academy.com/mocking-is-not-rocke t-science-basics-ae55d0aadf2b
  64. Unit tests without integration tests (Obligatory gif)

  65. Questions?

  66. Hadi Tok, CitizenMe @hadi_tok Thank you!