Mockk All the Things
Over the last few years Mockk has been gaining ground as the go-to mocking library in KotlinWorld ™. Just recently, it was listed as “adopt” in the ThoughtWorks technology Radar. Want to know what all the fuss is about?
Regular mocking
Let’s start with the basics. You can mock, spy and verify using this cute little DSL
import io.mockk.every
import io.mockk.mockk
import io.mockk.spyk
import io.mockk.verify
import org.junit.jupiter.api.Test
class ClockTest {
//sampleStart
@Test
fun `regular mock`() {
val clock = mockk()
every { clock.currentTime() } returns "7:20"
clock.currentTime()
verify { clock.currentTime() }
}
@Test
fun `regular spy`() {
val clock = spyk()
clock.currentTime()
verify { clock.currentTime() }
}
//sampleEnd
}
Mocks with behavior
No need to settle for just one fixed return value. You can add complex behavior to your mocks like this:
class ClockTest {
//sampleStart
@Test
fun `mock with complex behavior`() {
val clock = mockk()
every { clock.currentTime() } answers { dateFormat.format(Calendar.getInstance()) }
// ...
}
companion object {
val dateFormat = SimpleDateFormat("HH:mm")
}
//sampleEnd
}
There are a bunch of utility functions and properties you can use inside the answers
lambda to do things like calculate the response based on the function arguments. Full list here.
Mock chained calls
You can easily mock a chain of calls
class ClockTest {
//sampleStart
@Test
fun `mocking chained calls`() {
val oven = mockk()
every { oven.clock.currentTime() } returns "7:20"
//...
}
//sampleEnd
}
Mock hierarchies
You can achieve the same result using hierarchical mocking
class ClockTest {
//sampleStart
@Test
fun `hierarchical mocking`() {
val oven = mockk()
every { oven.clock } returns mockk {
every { currentTime() } returns "7:20"
}
//...
}
//sampleEnd
}
This is especially useful when mocking complex structures and to return collections of mocking objects, like in this example.
Mock objects
You can mock Objects as easily as you mock regular classes
class ClockTest {
//sampleStart
@Test
fun `object mocking`() {
mockkObject(UrlHelper)
every { UrlHelper.getBaseUrl() } returns URL("http://mockUrl.com")
//...
}
//sampleEnd
}
Mock Unit
You can mock functions that return Unit
using just Runs
class ClockTest {
//sampleStart
@Test
fun `mocking functions that return Unit`() {
val clock = mockk()
every { clock.changeBatteries() } just Runs
//...
}
//sampleEnd
}
Mock Nothing
Or functions that return Nothing. In which case you have to throw an exception as behavior (because a function that returns Nothing
never returns and can only end by throwing an exception, remember?)
class ClockTest {
//sampleStart
@Test
fun `mocking functions that return Nothing`() {
val clock = mockk()
every { clock.runForever() } throws Exception("called runForever")
//...
}
//sampleEnd
}
Mock extensions functions
You can mock extensions functions as well:
class ClockTest {
//sampleStart
@Test
fun `mocking functions extension functions`() {
with(mockk()) {
every { Duration.ofMinutes(5).startTimer() } returns true
//...
}
}
//sampleEnd
}
This works If the extension functions is defined on a class or an object. If it’s defined as a top level function instead, you can still mock it by following the advise in the next point 👇
Mocking top level functions
Got a top level function to mock? We’ve got you covered.
class ClockTest {
//sampleStart
@Test
fun `mocking top level functions`() {
mockkStatic("mockk.ModelsKt")
every { resolve(any()) } returns URL("http://mockk.com/users/1")
//...
}
//sampleEnd
}
Ok, you might need to check your classes to know exactly what to use as argument for mockkStatic
, but it’s no big deal.
Mock private functions
Yep, you can mock private functions by name.
class ClockTest {
//sampleStart
@Test
fun `mocking private functions`() {
val oven = mockk()
every { oven["lockDoor"]() } returns true
//...
}
//sampleEnd
}
You can even verify calls to private function by using recordPrivateCalls = true
Mock varargs
There’s also support for mocking functions that use varargs:
class VarargsTest {
//sampleStart
interface Calculator {
fun sumEverything(vararg num: Int): Int
}
@Test
fun `mocking varargs`() {
val calculator = mockk()
every { calculator.sumEverything(1, 2, 4) } returns 7
//...
every { calculator.sumEverything(1, *anyIntVararg(), 4) } returns 12
//...
every { calculator.sumEverything(1, *varargAllInt { it < 5 }) } returns 10
//...
}
//sampleEnd
}
And it’s not only the basics either. As you can see in the example you can do all kind of complex matchings.
Mock constructor
You can mock constructors. Useful for those times when you don’t actually control the object creation, but want to still be able to mock it.
class ClockTest {
//sampleStart
@Test
fun `mocking constructor`() {
mockkConstructor(Clock::class)
every { anyConstructed().currentTime() } returns "7:40"
assertEquals("7:40", Clock().currentTime())
}
//sampleEnd
}
Mock coroutines
If you’re working with coroutines and want to mock a suspending function you simply use coEvery
. In this example startTimer
is a suspending function:
class ClockTest {
//sampleStart
@Test
fun `mocking suspending functions`() {
val clock = mockk()
coEvery { clock.startTimer() } returns RUNNING
// ...
}
//sampleEnd
}
Along with coEvery
there’s a whole family of co...
functions (coAnswers
, coVerify
, coAssert
, etc.) for working with coroutines.
This is by no means a comprehensive guide. My intention was just to showcase same of the things that Mockk can do for you. For an in-depth introduction I recommend checking the “Mocking is not rocket science” series in Kotlin Academy.