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

Mocking Kotlin with MockK

A6c0dcba5f373b33df2c2c55540faab1?s=47 Hadi Tok
September 20, 2019

Mocking Kotlin with MockK

Introduction to the mocking library MockK

A6c0dcba5f373b33df2c2c55540faab1?s=128

Hadi Tok

September 20, 2019
Tweet

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!