Ktor Server Pro course is out 🔥Start learning today

A Guide to MockK: a Mocking Library for Kotlin

In this article, I'd like to show you how to use MockK- an open-source mocking library for Kotlin- with JUnit 5.
A featured image for category: Uncategorized

1. Introduction

There is no doubt, that testing is really crucial in the development process of any project. It allows us to automate the process of checking the functionality, as well as improves the quality of the code.

One of the techniques commonly used in unit testing is mocking. To put it in simple terms, mock objects are the objects that simulate the behavior of real objects.

In this article, I’d like to show you how to use MockK– an open-source mocking library for Kotlin- with JUnit 5.

2. Prepare the Code For Testing

Before we will head to the testing part, let’s write the code, which we will be testing later:

class ExampleClass {

    fun multiplyByTen(number: Int) = 10 * number

    fun publicFunction() = privateFunction()

    private fun privateFunction() = "Returned value"
}

class Injected {
    fun multiplyByFive(number: Int) = 5 * number
}

class ExampleClassWithDependency {

    lateinit var injected: Injected

    fun returnInjectedValue(number: Int) = injected.multiplyByFive(number)
}

object ExampleObject {
    fun concat(one: String, two: String) = one + two
}

As you can see, we’ve created a few simple classes and one object. Just for the record, a Kotlin object is a special singleton class- a class that has got only one instance.

3. Imports

As the next step, let’s add the required dependencies to our project:

  
  implementation("io.mockk:mockk:1.10.2")
  testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0")
  testImplementation("org.junit.jupiter:junit-jupiter-engine:5.7.0")

 

Image shows two ebooks people can get for free after joining newsletter

 

3. Using MockK library

In this article, we will learn how to use a few simple features of the library. If you would like to learn more, I highly recommend checking out MockK’s Github repository.

3.1. Mocking

Firstly, let’s see how to specify what exactly should be returned by the function of the mocked class:

    @Test
    fun `Mock a class`() {
        val exampleClass = mockk()

        every { exampleClass.publicFunction() } returns "Mocked value"

        val result = exampleClass.publicFunction()

        assertEquals("Mocked value", result)
    }

With this code, the “Mocked value” String will be returned for each invocation of the publicFunction.

Nextly, let’s mock a property of the class:

    @Test
    fun `Mock a property of the class`() {
        val injected = mockk()
        val exampleClass = ExampleClassWithDependency()
        exampleClass.injected = injected

        every { injected.multiplyByFive(any()) } returns 7

        val result = exampleClass.returnInjectedValue(10)

        assertEquals(7, result)
    }

This time, our result will be 7, even though the original function should return 50.

3.2. Relaxed mock

What happens if we do not specify the behavior of the invoked function? Let’s check this code:

    @Test
    fun `Example of MockKException`() {
        val exampleClass = mockk()

        exampleClass.publicFunction()
    }

The test failed throwing MockKException informing us that no answer has been found for the function we were trying to test.

    @Test
    fun `Fix MockKException using relaxed mock`() {
        val exampleClass = mockk(relaxed = true)
        val defaultStringValue = ""

        assertEquals(defaultStringValue, exampleClass.publicFunction())
    }

To fix that, we can either specify explicitly the behavior or use a relaxed mock. A relaxed mock returns the default values for all the functions within the mocked class. As we can see above, the default String value returned by the function will be an empty String.

3.3. Using Annotations

If we would like to simplify the creation of the object, we can use annotations. As we are using JUnit5, we need to annotate our test class with @ExtendWith:

@ExtendWith(MockKExtension::class)
internal class ExampleClassTest

As the next step, let’s add two properties to it:

    @MockK
    lateinit var annotatedMock: Injected

    @InjectMockKs
    var annotatedClass = ExampleClassWithDependency()

And create a new test:

    @Test
    fun `Simplified property mock with annotation`() {
        every { annotatedMock.multiplyByFive(any()) } returns 7

        val result = annotatedClass.returnInjectedValue(10)

        assertEquals(7, result)
    }

With this approach, we eliminate the need to manually create the mock for each test.

3.4. Spy The Object

Sometimes, we would like to have the possibility to check the real behavior of the class, as well as a mocked one. For that case, we can use a spy:

    @Test
    fun `Spy a class`() {
        val exampleClass = spyk()

        assertEquals("Returned value", exampleClass.publicFunction())
    }

As we can see here, the function returns the value that has been implemented in our code.

3.5. Mock Private Function Behavior

In our class implementation, publicFunction returns the value from privateFunction. Let’s check, how we can specify the behavior of the private function:

@Test
fun `Mock a private function`() {
val exampleClass = spyk(recordPrivateCalls = true)

every { exampleClass["privateFunction"]() } returns "Mocked value"

assertEquals("Mocked value", exampleClass.publicFunction())
}

3.6. Mock an Object

As we’ve told in the beginning, the Kotlin object is a special singleton class. To mock it, we will use mockkObject:

    @Test
    fun `Mock an object`() {
        mockkObject(ExampleObject)

        every { ExampleObject.concat(any(), any()) } returns "Mocked value"

        val result = ExampleObject.concat("", "")
        assertEquals("Mocked value", result)
    }

Even though Kotlin allows us to have only one instance of the object class, the MockK library allows us to create multiple instances of its mocks:

 
    @Test
    fun `Multiple mocked instances of object`() {
        val firstMock = mockk()
        val secondMock = mockk()

        every { firstMock.concat(any(), any()) } returns "One"
        every { secondMock.concat(any(), any()) } returns "Two"

        val firstResult = firstMock.concat("", "")
        val secondResult = secondMock.concat("", "")

        assertEquals("One", firstResult)
        assertEquals("Two", secondResult)
    }

3.7. Capturing

In our previous examples, we didn’t care about what arguments were passed to the stubbed methods. A technique called capturing allows us to validate what arguments has been passed:

    @Test
    fun `Capture passed value`() {
        val exampleClassMock = mockk()
        val argumentSlot = slot()

        every { exampleClassMock.multiplyByTen(capture(argumentSlot)) } returns 5

        exampleClassMock.multiplyByTen(55)

        assertEquals(55, argumentSlot.captured)
    }

As we can see, the slot contains the captured value, which we have passed to our checked function.

3.8. Verification

If we would like to check how many times the function has been invoked, we can use verify:

    @Test
    fun `Verify calls`() {
        val exampleClassMock = mockk()

        every { exampleClassMock.multiplyByTen(any()) } returns 5

        exampleClassMock.multiplyByTen(10)
        exampleClassMock.multiplyByTen(20)

        verify(exactly = 2) { exampleClassMock.multiplyByTen(any()) }
        confirmVerified(exampleClassMock)
    }

In the above example, we’ve used the exactly parameter to verify that the behavior happened exactly 2 times. If we would specify another number, the test would fail and inform us, that the verification failed.

4. Summary

And that would be all for this article. We’ve learned how to use some basic features of the MockK library with JUnit5.

For the source code of the project, please visit our project on GitHub.

If you enjoyed this tutorial, I would be forever grateful if you would like to share some feedback with me through our page, group, or contact form. I highly appreciate all the comments and suggestions that I am getting from you.

Share this:

Related content

Newsletter
Image presents 3 ebooks with Java, Spring and Kotlin interview questions.

Never miss any important updates from the Kotlin world and get 3 ebooks!

You may opt out any time. Terms of Use and Privacy Policy