Mockk: Better way to mock in Kotlin than Mockito

Prashant Pol
3 min readFeb 18, 2020

--

As we all know Mockito helped us a lot in unit testing. Its a best way to create mocks and use it in Java and Kotlin.

But it was actually built for Java, it’s having certain limitations while using with Kotlin.

And here comes the need to use another framework for mocking. It’s MOCKK.

For Kotlin 1.3+ dependency is,

testImplementation "io.mockk:mockk:{version}"

Sample code snippet is,

class User {
var address: Adddress
var contact: Adddress
}class MyTest {
@MockK
private lateinit var addressMock: Address
@MockK
private lateinit var contactMock: Contact
@InjectMockks
val user: User = User()

@Before
fun setUp() = MockkAnnotations.init(this, relaxUnitFun = true)
@Test
fun testFunction() {
......
......
}}

Now let’s see why Mockk becomes so special,

  1. Chained mocks:

Final result can be returned by specifying complete chain of calls. No need to mock each and every method output.

val product: Product = mockk()
every { product.currentOffer.isAcive } returns true

Suppose offer is of different types and its return type is generic then `hint` will help to give a hint to mockk which version we actually looking for,

val product: Product = mockk()
every {
product.currentOffer.hint(Offer.SpecialOffer::class).isAcive
} returns true

2. Object Mock:

A very exciting thing about Mockk is, objects can be mocked.

object Calculator {
fun add(a: Int, b:Int): Int {
}
.....
.....
}@Test
fun testMethod() {
mockkObject(Calculator)
every { Calculator.add(any(), any()) } returns 0
assertEquals(0, Calculator.add(1,2))
unmockkAll() // Or unmockkObject(Calculator)}

An interesting thing with mocking an object is,

Although object has single instance but mockk<Calculator>() can have multiple instances.

Even Enums can be mocked with this way.

3. Capturing:

Values can be captured using slot or mutableList .

val product:Product = mockk()
val slot = Slot<Date>()
every {
product.getCommentsPostedBy(capture(slot))
} answers {
println(slot)
}val today: Date = Date()val calendar = Calendar.getInstance()
calendar.add(Calendar.DAY_OF_YEAR, -daysAgo)
val yesterday = calendar.time
verify(exactly = 2) {
product.getCommentsPostedBy(or(today, yesterday))
}

4. Mocking suspend functions:

Mockk helps to mock suspend functions as well.

// Repository.kt
suspend fun getDataFromServer(
productId: String
): ServerData = withContext(Dispatchers.IO) {
......
}
// Test.kt@Test
fun testData() {
val repository: Repository = mockk()
val serverData: ServerData = ServerData()
coEvery { respository.getDataFromServer("p1") } retuns serverData}

5. Mocking extension functions

Mocking extension function

class Product{
.....
}
class Experiment { fun Product.newFunc(): Int {
....
}
}
// Testval experimentMock = mockk<Experiment>()
with(experimentMock) {
every { Product().newFunc() } returns 5
assertEuals(5, Product().newFunc()) verify { Product().newFunc() }
}

6. Mocking module:

Since kotlin allows to write functions directly in file without any class declaration, in such cases we need to mock entire file with mockStatic .

class Product {
}
// package.File.ktfun Product.newFunc() {
.....
}
// Test
mockStatic("package.FileKt")
every { Product().newFunc() } returns 5
assertEquals(5, Product().newFunc())verify(Product().newFunc())

7. Mock private functions:

It makes mocking of private functions very easy.

class Product {
fun firstFunc():Int = secondFunc()
fun secondFunc(): Int = 5
}
// Test
val product = spyk<Product>()
every { product["secondFunc"]() } returns 10
assertTrue(10, product.firstFunc())

If you want to verify calls to private functions then you need to set recordPrivateCalls=true while making spy.

val product = spyk<Product>(recordPrivateCalls = true)every { product["secondFunc"]() } returns 10
assertTrue(10, product.firstFunc())
verifySequence {
product.firstFunc()
product["secondFunc"]()
}

8. Even constructor can be mockked:

Unit testing becomes tedious if original code creates objects of a class and then make function calls within test target function.

It can be solved using constructor mocking.

class Product {
fun getNewClass(): Int = NewClass().get()
}
// Test
val product = spyk<Product>()
mockkContructor(NewClass::class)
every { anyContructed<NewClass>().get() } returns 5assertTrue(5, product.getNewClass())
verify { anyConstructed<NewClass>().get() }

--

--