Kotlin Unit Testing with Mockk

Marco Cattaneo
5 min readMar 29, 2020

--

It has been passed months since my last article on Kotlin coroutines lifecycle, and one year since an article about unit testing and mocking technique. Today I’m here to speak again about Unit Test focused on the mocking with the Kotlin library Mockk .

In object-oriented programming, mock objects are simulated objects that mimic the behaviour of real objects in controlled ways, most often as part of a software testing initiative

In this article I will not explain how to write a Unit Testing, I take it for granted, but I will explain how to use the mocking with same examples. Get some popcorn 🍿and let’s start!

https://giphy.com/gifs/reaction-demi-lovato-eating-LNUBwnTubPUHu

Mocking with Every and Verify

This is the most common scenario, in this case we want the test a class with a repository passed as argument, our goal is to verify the different behaviour of the test class with the different repository outputs.
In these samples we will use a MVP pattern divided in three parts:

  • The Presenter that do the business logic (we will test it)
  • The View, it’s a bridge between the producer (Presenter) and the consumer (who implements that View). For example in Android the View is implemented by an Activity where we can use the Android API to render the UI.
  • A Repository class, it’s the data source, in our sample it produces an array of objects,but for example it could fetch data from an API.

This is a Contract interface where we declare the two interfaces for View and Presenter:

This is the presenter:

And the repository class:

It’s quite simple, after fetchData() we make three things: fetch, map and pass the result to the consumer view.
In this first sample we want to:

  • Mock a response with an empty list
  • Verify onSuccess() is called
  • Verify the output is an empty list
Test the presenter with every and verify

In this sample we have two mocked parts: the repository and the view, on the first one we are mocking the data production with the every{}method in order to emulate an empty list of data or an output with a specific kind of data.
On the second we are using another method: verify{} in order to verify if a specific method is called (and how many times), in our sample it’s the callback interface where we provide the presenter output.

https://giphy.com/gifs/reactiongifs-Gpf8A8aX2uWAg

How create a mock for a method, where a callback is an argument

If your implementation is different and your repository doesn’t return anything but use an argument callback to communicate with the presenter the test is quite different, see the sample below:

A repository with a callback passed as argument

In this fetch method we are not returning a list of DataModel but we pass it on a callback (a lambda) passed as argument.

In this scenario the test is different, because we need to intercept (capture) the lambda, this lambda will receives the mocked output and allowing the presenter to continue the processing.

Testing with a repository callback

In this test you can see we are using the capture() method, by using it(and a slot<>() variable) we can intercept the callback passed inside the Presenter and provide to it the result. The slot<>() is a simple container where save the intercepted value. The capture slot is also used to intercept the result of a method to do an assertion for example.

How mock an Exception with every?

The way is the same, we can throw an exception inside the every{} and verify{} if the callback is invoked on the View . The Presenter is unchanged, an the test is this:

Test an Exception generate by every{}

What’s happening if every returns an RX-Java object?

We have seen how to use every{} if the method returns a value or use a callback to provide the result. But what happens if it returns an RXJava object like Observable , Single or other one? It’s simple, in the same way of return we can produce an output inside the Observable, look at the sample below:

The repository with the fetch method with a Single return:

and finally the test:

If we want to occur an Exception, the verify{}implementation is the same of previous sample, the only change is inside the every{} .

How mock an Error with RX

For the suspend function you can do the same test, I have written an article about that, click on the following link.

How mocking a static method?

If we want to test a class the best practices is to pass all resources we want to mock inside the constructor, but sometimes we need to mock a static method inside a object like an utils, third party sdk etc; so to have a perfect code coverage we need to write test with different behaviour of these static classes. How?

Fortunately Mockk has also a couple of methods to mock the static object, look at the example below:

This sample is similar to the first one, but we have changed method at line 11 where we generate the UUID for the UIDataModel,we are using a static method of MyUselessUtils and now we want to write a test where we can change the behaviour of this static method:

You can see that we are marking as mocked an entire object class (with the mockObject()method) and we are overriding the behaviour with the every{} as we have already seen in the previous chapter.

How verify a method on a not mocked class? Spy it!

Another common scenario is the necessity to use verify{} to verify if a method inside the testing class is called. If you try to do that you should receive an error like that:

io.mockk.MockKException: Failed matching mocking signature forleft matchers: [any(), any()]at io.mockk.impl.recording.SignatureMatcherDetector.detect(SignatureMatcherDetector.kt:99)
at io.mockk.impl.recording.states.RecordingState.signMatchers(RecordingState.kt:39)
at
[...]

This because you can invoke verify{} only on a mocked object. Anyway there is an implementation to do that, with the method: spyk() .

https://giphy.com/gifs/harriet-the-spy-pOr2IfTWtbq0w
Spyk example

This method allow you to spy the testing class and to invoke on it a verify{} method for example. For all questions there is the official documentation about it.

Thanks for reading

That’s all, I hope I helped you with this article, if you have other questions, please let me know! If you have liked this article don’t forget to 👏it. Thanks!

--

--

Marco Cattaneo
Marco Cattaneo

Responses (1)