SoatDev IT Consulting
SoatDev IT Consulting
  • About us
  • Expertise
  • Services
  • How it works
  • Contact Us
  • News
  • October 18, 2023
  • Rss Fetcher

The Road to Mockative 2

A Brief History of Kotlin Multiplatform

When Kotlin first came to the scene, it was purely a JVM language. This changed with the introduction of Kotlin/JS, Kotlin/Native, and Kotlin/Wasm, which allowed developers to use Kotlin to target just about anything. Targeting multiple of these platforms with the same Kotlin codebase became known as Kotlin Multiplatform (abbreviated as KMP).

When I first dipped my toes in Kotlin Multiplatform development in 2021, the team I joined had to resort to various uncommon solutions to overcome some of the challenges of this new, immature ecosystem. We have come a long way since then, and the developer experience is a lot smoother nowadays. JetBrains has done a lot of work to improve the experience, as has the community.

Mocking dependencies in Kotlin

The ability to mock dependencies is a powerful tool in many developers’ unit testing toolbelt. It is a key part that simplifies providing stub implementations of dependencies, enabling developers to test small parts of their code (units) separately. A mocking framework was one of the things that didn’t exist in the early days of Kotlin Multiplatform. You might think you could use any existing mocking solutions that work with Kotlin, such as MockK or Mockito. However, this is where the distinction between which platform you’re targeting using Kotlin becomes essential. You see, when targeting Kotlin/JVM, we have access to all the Java APIs, which includes nice things such as reflection, runtime proxies, and bytecode generation, none of which are available when targeting other platforms supported by Kotlin. However, Kotlin/JS can do a lot of the same due to the dynamic nature of JavaScript. Kotlin/Native, however, which is the target used when developing cross-platform native applications for both mobile and desktop, has only very limited reflection and is not dynamic in any way. These constraints and the fact that all existing solutions to mocking dependencies in Kotlin were using these features in one way or another meant that a new solution had to be developed. This paved the way for Mockative.

Introducing Mockative 2

Back in October 2021, I got the idea for Mockative. I threw together a proof-of-concept over the course of a few evenings and quickly realized the potential, so I polished up what I had built and shipped it a few weeks later. Now, almost two years later, Kotlin Multiplatform has come a long way, so it was time to have a fresh look at Mockative and see if I could improve on some of the nuisances and gotchas the API of Mockative 1 inherently suffered from. This meant a breaking change and, thus, a fresh new major version number.

So what changed?

In Mockative 1, when you wanted to mock a function through an invocation of a function it would look like this:

given(api).invocation { fetch("mockative/mockative") }
.thenReturn(mockative)

Not all that bad, right? I didn’t think so either, but the given(api) function combined with the receiver of the block passed to the invocation the function being the instance you’re calling a function on always looked strange to me, and I longed for an API similar to that of MockK with its’ every function. So, in Mockative 2, the same setup as above now looks like this:

every { api.fetch("mockative/mockative") }
.returns(mockative)

You may think this is such a seemingly insignificant change, and I partly agree with you. If this was the only change I wanted to introduce in Mockative 2, I may not have chosen to break the API like this. What I really wanted to change was the matcher API, which looks like this in the first version of Mockative:

given(api).function(api::fetch)
.whenInvokedWith(eq("mockative/mockative"))
.thenReturn(mockative)

A few of the things I disliked about this are:

  1. There’s no inherent link between api::fetch , and the argument passed to given(api). This leads to developer mistakes like writing given(api).function(Api::fetch) (notice the capital A), which may look right but wouldn’t work as expected because it’s expecting to mock a function accepting both an instance of Api and a String as its parameters.
  2. The whenInvokedWith function, while enabling a type-safe non-dynamic mocking that plays well with Kotlin/Native and, thus, Kotlin Multiplatform, it inherently decouples the matchers from the parameter names of the actual function call and thus makes it error-prone.

This is where the “inline” either/or matcher API of MockK and Mockito was very desirable. Still, I always thought it was too cumbersome to achieve in Kotlin/Native and, thus, Kotlin Multiplatform due to the strict nature of native development. In Mockative 2, that now looks like this:

every { api.fetch(any()) }
.returns(repository)

Which is deceptively close to the API of MockK and thus should feel very familiar to any developers using MockK already.

Throughout the lifetime of Mockative, I picked up on a few tricks other developers implemented in their solutions that made this kind of API possible on Kotlin/Native, specifically the fact that Kotlin, on any platform that’s not a JVM, does not perform type-checks on values until they’re actually used. This means that, on non-JVM platforms, we can make the any() function return any value we want, such as Unit and do a type-cast to the value our function accepts because the type-cast to a generic type is unchecked (just like on the JVM). For once, the non-JVM targets of Kotlin were the easiest ones to provide a solution for! For Kotlin/JVM, though, a type-check is performed for every non-generic argument passed to a function, and as such, this implementation results in ClassCastExceptions being thrown for all of our functions. To make matchers work on Kotlin/JVM, we would have to think of something fancier. Fortunately, those thoughts have already been thought by the developers behind MockK, Mockito, and MocKMP, which all served as inspiration and reference for the solution that is now present in Mockative 2.

So, if you’re a Kotlin Multiplatform developer and long for a way to mock dependencies in your tests across all the platforms you’re targeting, head over to the GitHub repository and try out the new API of Mockative2 today.

GitHub – mockative/mockative: Mocking for Kotlin/Native and Kotlin Multiplatform using the Kotlin Symbol Processing API (KSP)


Mocking in Kotlin Multiplatform was originally published in Better Programming on Medium, where people are continuing the conversation by highlighting and responding to this story.

Previous Post
Next Post

Recent Posts

  • Winning capital for your AI startup? Kleida Martiro is leading the conversation at TechCrunch All Stage
  • Nothing releases its first over-the-ear headphones, the $299 Headphone (1)
  • The electric Hummer is almost outselling the F-150 Lightning
  • Nothing releases their first over-the-ear headphones
  • Nothing launches its most expensive flagship yet, Phone (3)

Categories

  • Industry News
  • Programming
  • RSS Fetched Articles
  • Uncategorized

Archives

  • July 2025
  • June 2025
  • May 2025
  • April 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • October 2024
  • September 2024
  • August 2024
  • July 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023

Tap into the power of Microservices, MVC Architecture, Cloud, Containers, UML, and Scrum methodologies to bolster your project planning, execution, and application development processes.

Solutions

  • IT Consultation
  • Agile Transformation
  • Software Development
  • DevOps & CI/CD

Regions Covered

  • Montreal
  • New York
  • Paris
  • Mauritius
  • Abidjan
  • Dakar

Subscribe to Newsletter

Join our monthly newsletter subscribers to get the latest news and insights.

© Copyright 2023. All Rights Reserved by Soatdev IT Consulting Inc.