We all know Mockito is a very useful library in Unit Test. It is the best way to create mockups and use it in Java and Kotlin. But since it is a library built for Java, it has certain limitations when it is used for Kotlin. That is also the reason for the birth of MockK – a mocking framework written entirely in Kotlin and exclusively for Kotlin.
Like Mockito, MockK allows you to create and stub objects within your test code.
Mocking objects allows you to test on independent objects. Any dependency on the test object can be mocked to provide fixed conditions, thus ensuring the tests are always stable and clear.
Mockito is a popular framework used by Java developers and extremely powerful. But it does have a few annoyances when it comes to Kotlin. MockK, specially designed for Kotlin, will give us a much more comfortable experience.
For Kotlin 1.3 and above, we need to declare dependency (Kotlin DSL) for MockK as follows:
1 2 | <span class="token function">testImplementation</span> <span class="token punctuation">(</span> <span class="token string">"io.mockk:mockk:1.10.0"</span> <span class="token punctuation">)</span> |
The latest version of MockK so far is 1.10.0.
A sample code snippet is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token keyword">class</span> User <span class="token punctuation">{</span> <span class="token keyword">var</span> address <span class="token operator">:</span> Adddress <span class="token keyword">var</span> contact <span class="token operator">:</span> Contact <span class="token punctuation">}</span> <span class="token keyword">class</span> MyTest <span class="token punctuation">{</span> <span class="token annotation builtin">@MockK</span> <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> addressMock <span class="token operator">:</span> Address <span class="token annotation builtin">@MockK</span> <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> contactMock <span class="token operator">:</span> Contact <span class="token annotation builtin">@InjectMockks</span> <span class="token keyword">val</span> user <span class="token operator">:</span> User <span class="token operator">=</span> <span class="token function">User</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token annotation builtin">@Before</span> <span class="token keyword">fun</span> <span class="token function">setUp</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=</span> MockkAnnotations <span class="token punctuation">.</span> <span class="token function">init</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">,</span> relaxUnitFun <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token annotation builtin">@Test</span> <span class="token keyword">fun</span> <span class="token function">testFunction</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Now take a look at what this MockK library has in particular.
1. Chained mock:
Final result can be returned by specifying the complete sequence of calls. There is no need to simulate each method output.
1 2 3 | <span class="token keyword">val</span> product <span class="token operator">:</span> Product <span class="token operator">=</span> <span class="token function">mockk</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> every <span class="token punctuation">{</span> product <span class="token punctuation">.</span> currentOffer <span class="token punctuation">.</span> isAcive <span class="token punctuation">}</span> returns <span class="token boolean">true</span> |
Assuming the offer has many different types and its return type is generic, the hint
will help provide hints to mock the correct type we need.
1 2 3 4 5 | <span class="token keyword">val</span> product <span class="token operator">:</span> Product <span class="token operator">=</span> <span class="token function">mockk</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> every <span class="token punctuation">{</span> product <span class="token punctuation">.</span> currentOffer <span class="token punctuation">.</span> <span class="token function">hint</span> <span class="token punctuation">(</span> Offer <span class="token punctuation">.</span> SpecialOffer <span class="token operator">::</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> isAcive <span class="token punctuation">}</span> returns <span class="token boolean">true</span> |
2. Mock object:
The cool thing about MockK is that objects can also be mocked.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">object</span> Calculator <span class="token punctuation">{</span> <span class="token keyword">fun</span> <span class="token function">add</span> <span class="token punctuation">(</span> a <span class="token operator">:</span> Int <span class="token punctuation">,</span> b <span class="token operator">:</span> Int <span class="token punctuation">)</span> <span class="token operator">:</span> Int <span class="token punctuation">{</span> <span class="token operator">..</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token punctuation">.</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token annotation builtin">@Test</span> <span class="token keyword">fun</span> <span class="token function">testMethod</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">mockkObject</span> <span class="token punctuation">(</span> Calculator <span class="token punctuation">)</span> every <span class="token punctuation">{</span> Calculator <span class="token punctuation">.</span> <span class="token function">add</span> <span class="token punctuation">(</span> <span class="token function">any</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token function">any</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> returns <span class="token number">0</span> <span class="token function">assertEquals</span> <span class="token punctuation">(</span> <span class="token number">0</span> <span class="token punctuation">,</span> Calculator <span class="token punctuation">.</span> <span class="token function">add</span> <span class="token punctuation">(</span> <span class="token number">1</span> <span class="token punctuation">,</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token function">unmockkAll</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// Hoặc unmockkObject(Calculator)</span> <span class="token punctuation">}</span> |
One interesting thing about mocking an object is that even though the object has only one instance, mockk<Calculator>()
can have many different instances. Even enums can be mocked this way.
3. Capturing
Object values / properties can be captured using a slot
or mutableList
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">val</span> product <span class="token operator">:</span> Product <span class="token operator">=</span> <span class="token function">mockk</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">val</span> slot <span class="token operator">=</span> Slot <span class="token operator"><</span> Date <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> every <span class="token punctuation">{</span> product <span class="token punctuation">.</span> <span class="token function">getCommentsPostedBy</span> <span class="token punctuation">(</span> <span class="token function">capture</span> <span class="token punctuation">(</span> slot <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> answers <span class="token punctuation">{</span> <span class="token function">println</span> <span class="token punctuation">(</span> slot <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">val</span> today <span class="token operator">:</span> Date <span class="token operator">=</span> <span class="token function">Date</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">val</span> calendar <span class="token operator">=</span> Calendar <span class="token punctuation">.</span> <span class="token function">getInstance</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> calendar <span class="token punctuation">.</span> <span class="token function">add</span> <span class="token punctuation">(</span> Calendar <span class="token punctuation">.</span> DAY_OF_YEAR <span class="token punctuation">,</span> <span class="token operator">-</span> daysAgo <span class="token punctuation">)</span> <span class="token keyword">val</span> yesterday <span class="token operator">=</span> calendar <span class="token punctuation">.</span> time <span class="token function">verify</span> <span class="token punctuation">(</span> exactly <span class="token operator">=</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> product <span class="token punctuation">.</span> <span class="token function">getCommentsPostedBy</span> <span class="token punctuation">(</span> <span class="token function">or</span> <span class="token punctuation">(</span> today <span class="token punctuation">,</span> yesterday <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
4. Mocking both suspend function:
Mockk also supports mock suspend functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token comment">// Repository.kt</span> <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">getDataFromServer</span> <span class="token punctuation">(</span> productId <span class="token operator">:</span> String <span class="token punctuation">)</span> <span class="token operator">:</span> ServerData <span class="token operator">=</span> <span class="token function">withContext</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token punctuation">}</span> <span class="token comment">// Test.kt</span> <span class="token annotation builtin">@Test</span> <span class="token keyword">fun</span> <span class="token function">testData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> repository <span class="token operator">:</span> Repository <span class="token operator">=</span> <span class="token function">mockk</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">val</span> serverData <span class="token operator">:</span> ServerData <span class="token operator">=</span> <span class="token function">ServerData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> coEvery <span class="token punctuation">{</span> respository <span class="token punctuation">.</span> <span class="token function">getDataFromServer</span> <span class="token punctuation">(</span> <span class="token string">"p1"</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> retuns serverData <span class="token punctuation">}</span> |
5. Mocking both Extension functions
Mocking extension function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">class</span> Product <span class="token punctuation">{</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> Experiment <span class="token punctuation">{</span> <span class="token keyword">fun</span> Product <span class="token punctuation">.</span> <span class="token function">newFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> Int <span class="token punctuation">{</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Test</span> <span class="token keyword">val</span> experimentMock <span class="token operator">=</span> mockk <span class="token operator"><</span> Experiment <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">with</span> <span class="token punctuation">(</span> experimentMock <span class="token punctuation">)</span> <span class="token punctuation">{</span> every <span class="token punctuation">{</span> <span class="token function">Product</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">newFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> returns <span class="token number">5</span> <span class="token function">assertEuals</span> <span class="token punctuation">(</span> <span class="token number">5</span> <span class="token punctuation">,</span> <span class="token function">Product</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">newFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> verify <span class="token punctuation">{</span> <span class="token function">Product</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">newFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
6. Mocking modules:
Since Kotlin allows to write functions directly in the file without declaring any class, in such cases we need to mock the entire file with mockStatic
.
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">class</span> Product <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token comment">// package.File.kt</span> <span class="token keyword">fun</span> Product <span class="token punctuation">.</span> <span class="token function">newFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span> <span class="token operator">..</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token comment">// Test</span> <span class="token function">mockStatic</span> <span class="token punctuation">(</span> <span class="token string">"package.FileKt"</span> <span class="token punctuation">)</span> every <span class="token punctuation">{</span> <span class="token function">Product</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">newFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> returns <span class="token number">5</span> <span class="token function">assertEquals</span> <span class="token punctuation">(</span> <span class="token number">5</span> <span class="token punctuation">,</span> <span class="token function">Product</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">newFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token function">verify</span> <span class="token punctuation">(</span> <span class="token function">Product</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">newFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> |
7. Mock private function:
MockK makes mocking private methods very easy.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">class</span> Product <span class="token punctuation">{</span> <span class="token keyword">fun</span> <span class="token function">firstFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token function">secondFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">fun</span> <span class="token function">secondFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token number">5</span> <span class="token punctuation">}</span> <span class="token comment">// Test</span> <span class="token keyword">val</span> product <span class="token operator">=</span> spyk <span class="token operator"><</span> Product <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> every <span class="token punctuation">{</span> product <span class="token punctuation">[</span> <span class="token string">"secondFunc"</span> <span class="token punctuation">]</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> returns <span class="token number">10</span> <span class="token function">assertTrue</span> <span class="token punctuation">(</span> <span class="token number">10</span> <span class="token punctuation">,</span> product <span class="token punctuation">.</span> <span class="token function">firstFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> |
If you want to verify calls to private methods then you need to set recordPrivateCalls=true
while creating spy
.
1 2 3 4 5 6 7 8 | <span class="token keyword">val</span> product <span class="token operator">=</span> spyk <span class="token operator"><</span> Product <span class="token operator">></span> <span class="token punctuation">(</span> recordPrivateCalls <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> every <span class="token punctuation">{</span> product <span class="token punctuation">[</span> <span class="token string">"secondFunc"</span> <span class="token punctuation">]</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> returns <span class="token number">10</span> <span class="token function">assertTrue</span> <span class="token punctuation">(</span> <span class="token number">10</span> <span class="token punctuation">,</span> product <span class="token punctuation">.</span> <span class="token function">firstFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> verifySequence <span class="token punctuation">{</span> product <span class="token punctuation">.</span> <span class="token function">firstFunc</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> product <span class="token punctuation">[</span> <span class="token string">"secondFunc"</span> <span class="token punctuation">]</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
8. Even the constructor can be mocked:
Unit testing becomes very frustrating if the original code creates the objects of a class and then executes the function calls in the test target function. It can be solved using mock constructor.
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">class</span> Product <span class="token punctuation">{</span> <span class="token keyword">fun</span> <span class="token function">getNewClass</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token function">NewClass</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// Test</span> <span class="token keyword">val</span> product <span class="token operator">=</span> spyk <span class="token operator"><</span> Product <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">mockkContructor</span> <span class="token punctuation">(</span> NewClass <span class="token operator">::</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> every <span class="token punctuation">{</span> anyContructed <span class="token operator"><</span> NewClass <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> returns <span class="token number">5</span> <span class="token function">assertTrue</span> <span class="token punctuation">(</span> <span class="token number">5</span> <span class="token punctuation">,</span> product <span class="token punctuation">.</span> <span class="token function">getNewClass</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> verify <span class="token punctuation">{</span> anyConstructed <span class="token operator"><</span> NewClass <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Source: https://medium.com/@prashantspol/mockk-better-to-way-to-mock-in-kotlin-than-mockito-1b659c5232ec