MockK - nifty mock features I recently discovered

xetra11

Patrick Charles Höfer

Posted on October 19, 2021

MockK - nifty mock features I recently discovered

I am using MockK (https://mockk.io/) for a while now in all my Kotlin applications. But just recently I discovered some features I wish I knew beforehand because they really reduce the verbosity of my unit test.

As a developer I am lazy. Therefore I just looked up MockK features I needed at the time without discovering all the other mentioned advanced features. I felt like I will find them when I need them. To be honest - I should have spent some time reading the documentation. Next time I definitely will!

Here is a simple example that improved in readability thanks to some research on MockK features.

Logic to Test

Here is the logic I wanted to test

    fun dispatchEvent() {
        val taskPayload = Json.encodeToString(TaskPayload("123"))
        val gameEvent: GameEvent = GameEvent(UUID.randomUUID().toString(), "task", DateTime.now(), taskPayload)
        val topic = "projects/barbarus-game/topics/task"
        LOG.info("dispatch event to topic $topic")
        pubSubTemplate.publish(topic, gameEvent).addCallback({ LOG.info(it) }, { LOG.error("failure") })
    }
Enter fullscreen mode Exit fullscreen mode

Unit Test Iteration #0

And here is my initial unit test:

    internal class EventDispatcherTest {
    private val pubSubTemplate: PubSubTemplate = mockk()
    private val eventDispatcher: EventDispatcher = EventDispatcher(pubSubTemplate)

    @Test
    fun `should publish game event message`() {
        val testTopic = "projects/barbarus-game/topics/task"

        every { pubSubTemplate.publish(eq(testTopic), any<GameEvent>()) } returns mockk() {
            every { addCallback(any(), any()) } returns Unit
        }

        eventDispatcher.dispatchEvent()

        verify { pubSubTemplate.publish(eq(testTopic), any<GameEvent>()) }
    }
}
Enter fullscreen mode Exit fullscreen mode

I simply want to see if the dispatchEvent() function does call the PubSubTemplate#publish function as expected.

However MockK#verify does not work withou the pubSubTemplate mock being defined with a proxy. To satisfy the test runner I added an every for pubSubTemplate and also had to satisfy the return value (or answer in mockk-domain language). The return value is of type ListenableFuture. However I do not need any specific behaviour from it so I just mocked it away.

[...] returns mockk() {
            every { addCallback(any(), any()) } returns Unit
        }

Enter fullscreen mode Exit fullscreen mode

This is the part where I mock the ListenableFuture.

All of that I had to do, to simply just use verify and assert that my dispatchEvent function is calling the PubSubTemplate#$publish function with the expected topic string.

I did a quick google research to see what I can do better.

Unit Test Iteration #1

The first thing I was wondering about was, if there is a MockK idiomatic way to mock a function that returns Unit.

And I found this sweet article/documentation:

https://notwoods.github.io/mockk-guidebook/docs/mockito-migrate/void/

So instead of using returns Unit one can simply replace every with justRun.

The statement now looks a bit slicker

        every { pubSubTemplate.publish(eq(testTopic), any<GameEvent>()) } returns mockk() {
            justRun { addCallback(any(), any()) }
        }
Enter fullscreen mode Exit fullscreen mode

But wait! There is more!

Unit Test Iteration #2

So still I am unhappy with verify being dependant on the every mock of pubSubTemplate. I then stumbled over https://mockk.io/#relaxed-mock

I had no idea what "relaxed" might imply in the context of unit testing. So I took a little research. It says "allows creation with no specific behaviour". So all functions are returning "Simple Values" by default - so me as a developer doesn't need to implement a behaviour with every for the called functions of that unit under test.

The only thing I needed to do was to call the mockk() function with relaxed = true argument.


internal class EventDispatcherTest {
    private val pubSubTemplate: PubSubTemplate = mockk(relaxed = true)
    private val eventDispatcher: EventDispatcher = EventDispatcher(pubSubTemplate)

    @Test
    fun `should publish game event message`() {
        val testTopic = "projects/barbarus-game/topics/task"
        eventDispatcher.dispatchEvent()
        verify { pubSubTemplate.publish(eq(testTopic), any<GameEvent>()) }
    }
}
Enter fullscreen mode Exit fullscreen mode

And voila the simple verify assertion does it job without the need of a behaviour definition of the mocked class.

Final Words

I hope I could help one or two developers with this. I'd wish I knew it earlier but hey - better now than never eh?

💖 💪 🙅 🚩
xetra11
Patrick Charles Höfer

Posted on October 19, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related