Mocking extensions from Continuation

Answer #1 100 %

Here is a small investigation. If you are just looking for solution for inline functions and inline classes, scroll to solution section.

Long explanation:

These are tricky consequences of modern kotlin features. Let's decompile this code to java with help of kotlin plugin. This mockContinuation.resumeWithException(any()) becomes something like this (shortened and beautified version)

Matcher matcher = (new ConstantMatcher(true));
Throwable anyThrowable = (Throwable)getCallRecorder().matcher(matcher, Reflection.getOrCreateKotlinClass(Throwable.class));
Object result = kotlin.Result.constructor-impl(ResultKt.createFailure(anyThrowable));

As you can see a few things happened. First, there is no call to resumeWithException anymore. Because it is an inline function, it was inlined by the compiler, so now it's a resumeWith call. Second, matcher returned by any() was wrapped with a mysterious call kotlin.Result.constructor-impl(ResultKt.createFailure(anyThrowable)), and it's not an argument of a function, called on the mock. That's why mockk can't match the signature and the matcher. Obviously we can try to fix it by mocking resumeWith function itself:

every { mockContinuation.resumeWith(any()) } just Runs

And it does not work either! Here is the decompiled code:

Matcher matcher = (new ConstantMatcher(true));
Object anyValue = getCallRecorder().matcher(matcher, Reflection.getOrCreateKotlinClass(kotlin.Result.class));

And here is another mysterious call unbox-impl(). Let's look at Result class definition

public inline class Result @PublishedApi internal constructor(
    internal val value: Any?

It's an inline class! And ubox-impl() is a compiler-generated function like this:

public final Object unbox-impl() {
   return this.value;

Basically, the compiler inlines Result object, by replacing it with it's value. So again, instead of calling resumeWith(any()) in the end we call resumeWith(any().value) and mocking library is confused. So how to mock it? Remember, mockContinuation.resume(any()) worked for some reason, even though resume is just another inline function

public inline fun  Continuation.resume(value: T): Unit =

Decompiling mockContinuation.resume(any()) gives us

Object anyValue = getCallRecorder().matcher(matcher, Reflection.getOrCreateKotlinClass(Unit.class));
Object result = kotlin.Result.constructor-impl(anyValue);

As we can see, it was inlined indeed and resumeWith was called with a result object, not with anyValue, which is our matcher. But, let's take a look at this mysterious kotlin.Result.constructor-impl:

public static Object constructor-impl(Object value) {
      return value;

So it actually does not wrap the value, juts return it! That's why it actually works and gives us a solution how to mock resumeWith:

     every { mockContinuation.resumeWith(Result.success(any())) } just Runs

Yes, we are wrapping our matcher into Result, which as we saw, gets inlined. But what if we want to distinguish between Result.success() and Result.failure()? We still can't mock mockContinuation.resumeWith(Result.failure(any())), because failure() call wraps the argument into something else (check the source code or decompiled code above). So I can think about something like that:

     every { mockContinuation.resumeWith(Result.success(any())) } answers {
            val result = arg(0)
            if (result is Unit) {
            } else {

The result value is instance of either our type (Unit in this case) or Result.Failure type, which is an internal type.


  1. Mocking inline function is generally impossible, because they are inlined at compile time and mocking runs later at runtime. Mock functions, which are called inside inlined function instead.
  2. When dealing with inline classes, match inlined value, not the wrapper. So instead of mock.testFunction(any()) use mock.testFunction(InlinedClass(any())).

Here is a feature request for mockk to support inline classes, which is currently in Opened state.

You’ll also like:

© 2023 -