Waldo sessions now support scripting! – Learn more
Testing

How to Use Mockk Spy in a Test

Pius Aboyi
Pius Aboyi
How to Use Mockk Spy in a Test
January 10, 2023
7
min read

Sometimes you may need to test a method that depends on another method or class. This type of situation can increase the complexity of your test.

With a testing tool like Mockk, you can mock dependency classes so that you focus on the actual class under test.

In this post, you'll learn what Mockk Spy is and how to use it in your tests.

What Is Mockk?

Mockk is a mocking library with first-class support for Kotlin.

You can also define Mockk as an alternative to Mockito, which is a popular mocking framework for Java.

Mockk improves on Mockito in terms of Kotlin usage in several ways. For example, Mockk makes it easier to mock final classes.

What Is Spy in Mockk?

Mockk Spy makes it possible to customize the behavior of methods in the object you're mocking while still being capable of running the actual method. In testing, a spy generally refers to a real instance of a class or object that can be stubbed.

Mockk provides a spyk() function that you can use to create a new spy instance of a class.

Before we continue to the tutorial on how to use Mockk in tests, let's discuss some of the differences between spies and stubs.

Differences Between Spies and Stubs in Unit Testing

The following are some differences between spies and stubs.

  • A spy represents an instance of an object with a copy of all the properties in the object. A stub, on the other hand, contains steps that determine how tests respond to method calls.
  • It's possible to call methods in a spy object without altering their behavior, while a stub only provides a temporary replacement to the behavior of a method.
  • Stubs and spies work together such that methods in a spy can be stubbed.

How to Use Mockk Spy in a Test

Now, let's walk through a guide on how to use Mockk Spy in a test. For this tutorial, we'll be using Mockk to write unit tests for an Android application written in Kotlin.

Step 1: Project Setup

In order to follow along, you'll first need to create a new Android Studio project. Alternatively, you can use an existing project that you already have on your computer.

Once you have your project, create a new Kotlin class under the main package and save it as Utils.

Add the following methods to the new class:


    fun add(a: Int, b: Int): Int {
        return a+b
    }
    
    fun findAverage(a: Int, b: Int): Int {
        val total = add(a,b)
        return total/2;
    }

From the code sample above, the first method simply adds two numbers. The second method finds the average value of two numbers. That is to say, it uses the first add() method to add the parameters, then it divides the result by 2.

Later in this guide, we'll see how we can create a spy for this class.

Step 2: Add Mockk Dependency to Your Project

Before you can use Mockk, you'll need to add the Gradle dependency to your project. To do this, open the app-level build.gradle file for your project and add the following code to the dependencies section:

    
        dependencies {
            testImplementation 'junit:junit:4.13.2'
            testImplementation 'io.mockk:mockk:1.13.3'
        }

Once you're done, hit the Sync Now button that appears after modifying the Gradle file to finish the installation.

Step 3: Write Unit Test with Spy

Now that we have Mockk installed and the class we wish to test, let's write our first test. Open the test source set folder and create a new class for our unit tests. Save the class as UtilsTest.

Next, add the following method with the @Test annotation to the class so that the code for the class looks like this:

    
        class UtilsTest {

            @Test
            fun testSpy() {
                //Arrange
                val spy = spyk<Utils>()
        
                //Act
                val result = spy.findAverage(5,5)
        
                //Assert
                assertEquals(5, result)
            }
        }
    

The code above sets up a unit test that builds a spy of the Utils class. Next, the result variable fires the spy copy of the findAverage() method. Then, the last line verifies whether the result meets an expectation of 5.

This test should pass, as calling findAverage() will return the result of adding 5 plus 5, which is 10, and 10 divided by 2 equals 5.

From the test above, we created a spy but didn't alter the behavior of any of the methods. So, let's write another test demonstrating how to use a spy to change the behavior of a method.

Add the following method just after the testSpy() method:

    
        @Test
        fun testSpyCustom() {
            //Arrange
            val spy = spyk<Utils>()
            every { spy.add(any(), any()) } returns 10
        
            //Act
            val result = spy.findAverage(15,5)
        
            //Assert
            Assert.assertEquals(5, result)
        }
    

The testSpyCustom() method is very similar to the previous method except for the introduction of the line with "every { spy.add(any(), any()) } returns 10".

This line means that for every time the add() function is called, your test will ignore the original implementation of the function and return the value 10 instead. As a result, this test will always pass no matter the value you pass to the findAverage() method.

Step 4: Run the Tests

To run a test, simply tap on the green play button next to each test method. Then, wait for Android Studio to finish running the test.

If you wish to run all tests at once, you can hit the green play button near the class declaration instead.

Mockk makes it easy to test private methods

Step 5: Testing Private Methods in Mockk

Mockk makes it easy to test private methods. In order to demonstrate this, let's add a new private subtract method to the Utils class by adding the following after the findAverage() method:

    
        private fun subtract(a: Int, b: Int): Int {
            return a-b;
        }
        
        fun inverseSubtract(a: Int, b: Int): Int {
            return subtract(b, a)
        }
    

Now, add a new test for this private method in the UtilsTest.kt file using the following code:

    
        @Test
        fun testPrivateMethod() {
            // Arrange
            val mock = spyk<Utils>(recordPrivateCalls = true)
            every { mock["subtract"](any<Int>(), any<Int>()) } answers { firstArg<Int>() - 10 }
        
            // Act
            val result = mock.inverseSubtract(1, 30)
        
            // Assert
            assertEquals(20, result)
        }
    

The code above creates a mock capable of stubbing the private method by setting the value of the recordPrivateCalls parameter to true.

Then, in order to access and alter the behavior of the private method, the name of the method is provided as a string inside the square brackets.

Next, the behavior is altered such that on every call of the subtract method, it returns a-10 instead of a-b.

Conclusion

To wrap things up, here's a summary of everything we covered in this post and some recommendations for what to do next.

First, we defined what Mockk and Mockk Spy are. Mockk is a testing framework with first-class support for Kotlin, while Mockk Spy is a feature of this framework that makes it possible to create spy objects and implement custom behavior when methods within the objects are called.

Next, we walked through a guide with code samples on how to use Mockk Spy in unit tests for a Kotlin project.

In our example, we wrote an automated test for a simple Android mobile app that makes use of JUnit.

This type of test requires coding skills to configure, write, and run. As a result, it can be time-consuming and limit who can perform the test.

Waldo offers a no-code tool for testing mobile apps that might be a more friendly alternative.

Mockk can be applied in different use cases. For example, you can also use Mockk to mock complex classes in instrumentation tests like end-to-end tests with Espresso.

To keep learning about Mockk, check out this article here.

Automated E2E tests for your mobile app

Waldo provides the best-in-class runtime for all your mobile testing needs.
Get true E2E testing in minutes, not months.

Reproduce, capture, and share bugs fast!

Waldo Sessions helps mobile teams reproduce bugs, while compiling detailed bug reports in real time.