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") })
    }
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>()) }
    }
}
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
        }
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()) }
        }
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>()) }
    }
}
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?
 

 
    
Top comments (0)