Repository: mockito/mockito
Issue: Kotlin Result seems to behave weirdly when trying to be returned with Mockito

I am facing an issue when trying to use the Kotlin Result in my project. I am trying to mock the return value as shown here:
```kotlin
`when`(useCase.getA()).thenReturn(Result.success(ObjectA("testName")))
```
The exception when trying to use that returned Result is this one: `java.lang.ClassCastException: kotlin.Result cannot be cast to ObjectA`

An interesting thing I found when debugging is that if I check the returned value of that `useCase.getA()` function, it returns an object with description `Success(Success(ObjectA(name=testName)))`. A regular Result returns this description `Success(ObjectA(name=name))`. I guess that's the reason it's failing, it seems there is like a nested Result object, so I am assuming there is something wrong with Mockito when handling the Kotlin Result class.

This is a [gist](https://gist.github.com/adrianhescalante/30449c876608fb7283e52c4d6acc5ad6)  I created to test it. There are two tests, one with the Kotlin Result and another one with a quick Result implementation (OwnResult) to check there is not something wrong I am doing myself.

I am using Mockito 3.3.3 on an Android project with Java 8.

Issue comments:
@adrianhescalante You can try mockk instead if you use kotlin
Yes, I migrated the project to Mockk in the end. Thank you.
For Kotlin projects, we now recommend `mockito-kotlin`: https://github.com/mockito/mockito-kotlin If you can still reproduce this issue, please open an issue over there and we can investigate further.

Pull request review comments:
These appear to be unavailable. Can we remove these and the annotations?
The same would be required here, assuming that `unbox-impl` also has a pre-known method signature?
I think we can use `org.mockito.internal.stubbing.answers.InvocationInfo` here, in a similar fashion to how `Returns` uses it: https://github.com/mockito/mockito/blob/80374488162380d006b8ae6da915d940cbf68c7c/src/main/java/org/mockito/internal/stubbing/answers/Returns.java#L32

In that case, we can augment `Returns.answer` to use the `InvocationInfo` API and we don't need to update the `OngoingStubbingImpl` interface. E.g. we move `unboxUnderlyingValueIfNeeded` into `Returns.answer`. We can then lazily compute `value` when it is invoked, to be minimally computational impactful.
In this case, yes I think we should ideally wait for Kotlin 1.5 to ship. Of course we need to be compatible with previous Kotlin versions. Therefore, we could check if the annotation exists on the class and return that?

Since `Class.getAnnotation` takes a `Class`, the annotation would be on the classpath. Therefore, we can optimize `unboxUnderlyingValueIfNeeded` to only run when the annotation is available. E.g. we load the class based on its string name in a `static {}` block into a field. If that throws a `ClassNotFoundException`, the `Class<?>` field remains `null` and we can short-circuit `unboxUnderlyingValueIfNeeded` to bail if the class is `null`. We use a similar function for some JVM-internals in https://github.com/mockito/mockito/blob/80374488162380d006b8ae6da915d940cbf68c7c/src/main/java/org/mockito/internal/exceptions/stacktrace/StackTraceFilter.java#L23-L39
If all inline classes have the annotation, is this still necessary? If I am not mistaken, we can update this method to the following:

```java
public static boolean isInlineClassWithAssignableUnderlyingType(
            Class<?> inlineClass, Class<?> underlyingType) {
    return jvmInlineAnnotation != null
                     && inlineClass.isAnnotationPresent(jvmInlineAnnotation);
}
```
Nit:

```java
if (value == null || jvmIlineAnnotation == null) {
    return value;
}
```

Then we can remove the if-statement down below and improve the indentation by 1.
I am assuming that all inline classes have the annotation and also the `unbox-impl` method, do they not? In that case, I think we should throw an explicit exception here that this scenario is unexpected and Mockito can't recover (as returning `boxedValue` here will be wrong, per the original issue. You can add a new exception method to https://github.com/mockito/mockito/blob/release/3.x/src/main/java/org/mockito/internal/exceptions/Reporter.java
Just to double-check: when reading https://kotlinlang.org/docs/inline-classes.html#members it mentions the following sentence:

> To declare an inline class, use the value modifier before the name of the class:

The example in question does not include the `@JvmInline` annotation. Does this mean that Kotlin inline classes don't necessarily have the annotation available?

If that's the case, then we should probably explicitly call this out in our documentation so that users are aware of this requirement.
Yes, otherwise, it would be possible to mock a function with different inline class:
```
inline class ICAny(val a: Any)
inline class ICInt(val i: Int)

fun foo(): ICInt

`when`(foo()).thenReturn(ICAny)
```
will try to unbox and then cast the result, failing.
Well, long story short:
On JVM, they are required to have the annotation
On other backends (Native and JS) they do not.

So, for our use-case, we can safely assume, that they do have the annotation.
Oooh I see. Yes that makes sense now. Could we then update the comment to make that a bit clearer?

```java
// Make sure that the current inline class is also the class that is compatible with the underlying type.
// If we don't make this check, then we would potentially pass a mock of inline type A into a method
// that accepts inline type B.
```

Patch that resolved the issue:
diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle
index 570e6dcd21..25bfecbe66 100644
--- a/gradle/dependencies.gradle
+++ b/gradle/dependencies.gradle
@@ -36,7 +36,7 @@ libraries.bndGradle =  'biz.aQute.bnd:biz.aQute.bnd.gradle:5.2.0'
 
 libraries.groovy = 'org.codehaus.groovy:groovy:3.0.8'
 
-def kotlinVersion = '1.4.32'
+def kotlinVersion = '1.5.0'
 libraries.kotlin = [
     version: kotlinVersion,
 
diff --git a/src/main/java/org/mockito/internal/exceptions/Reporter.java b/src/main/java/org/mockito/internal/exceptions/Reporter.java
index 5b344837b9..6983518c2e 100644
--- a/src/main/java/org/mockito/internal/exceptions/Reporter.java
+++ b/src/main/java/org/mockito/internal/exceptions/Reporter.java
@@ -1032,6 +1032,15 @@ public static MockitoException notAnException() {
                         "This may happen with doThrow(Class)|thenThrow(Class) family of methods if passing null parameter."));
     }
 
+    public static MockitoException inlineClassWithoutUnboxImpl(
+            Class<?> inlineClass, Exception details) {
+        return new MockitoException(
+                join(
+                        "Kotlin inline class should have unbox-impl() method,",
+                        "but " + inlineClass + " does not."),
+                details);
+    }
+
     public static UnnecessaryStubbingException formatUnncessaryStubbingException(
             Class<?> testClass, Collection<Invocation> unnecessaryStubbings) {
         StringBuilder stubbings = new StringBuilder();
diff --git a/src/main/java/org/mockito/internal/stubbing/answers/Returns.java b/src/main/java/org/mockito/internal/stubbing/answers/Returns.java
index 184f879204..98afb4620c 100644
--- a/src/main/java/org/mockito/internal/stubbing/answers/Returns.java
+++ b/src/main/java/org/mockito/internal/stubbing/answers/Returns.java
@@ -9,6 +9,7 @@
 
 import java.io.Serializable;
 
+import org.mockito.internal.util.KotlinInlineClassUtil;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.mockito.stubbing.ValidableAnswer;
@@ -23,7 +24,7 @@ public Returns(Object value) {
     }
 
     public Object answer(InvocationOnMock invocation) throws Throwable {
-        return value;
+        return KotlinInlineClassUtil.unboxUnderlyingValueIfNeeded(invocation, value);
     }
 
     @Override
@@ -38,7 +39,10 @@ public void validateFor(InvocationOnMock invocation) {
                     invocationInfo.printMethodReturnType(), "null", invocationInfo.getMethodName());
         }
 
-        if (!returnsNull() && !invocationInfo.isValidReturnType(returnType())) {
+        if (!returnsNull()
+                && !invocationInfo.isValidReturnType(returnType())
+                && !KotlinInlineClassUtil.isInlineClassWithAssignableUnderlyingType(
+                        returnType(), invocationInfo.getMethod().getReturnType())) {
             throw wrongTypeOfReturnValue(
                     invocationInfo.printMethodReturnType(),
                     printReturnType(),
diff --git a/src/main/java/org/mockito/internal/util/KotlinInlineClassUtil.java b/src/main/java/org/mockito/internal/util/KotlinInlineClassUtil.java
new file mode 100644
index 0000000000..11bd5e8284
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/KotlinInlineClassUtil.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2021 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util;
+
+import org.mockito.internal.exceptions.Reporter;
+import org.mockito.internal.stubbing.answers.InvocationInfo;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class KotlinInlineClassUtil {
+    private static Class<Annotation> jvmInlineAnnotation;
+
+    static {
+        try {
+            jvmInlineAnnotation = (Class<Annotation>) Class.forName("kotlin.jvm.JvmInline");
+        } catch (ClassNotFoundException e) {
+            // Do nothing: kotlin is pre 1.5.0
+        }
+    }
+
+    // When mocking function, returning inline class, its return type is
+    // underlying type.
+    // So, `thenReturn` calls fails, because of non-compatible types.
+    public static boolean isInlineClassWithAssignableUnderlyingType(
+            Class<?> inlineClass, Class<?> underlyingType) {
+        try {
+            // Since 1.5.0 inline classes have @JvmInline annotation
+            if (jvmInlineAnnotation == null
+                    || !inlineClass.isAnnotationPresent(jvmInlineAnnotation)) return false;
+
+            // All inline classes have 'box-impl' method, which accepts
+            // underlying type and returns inline class.
+            // Make sure that the current inline class is also the class that is compatible with the
+            // underlying type.
+            // If we don't make this check, then we would potentially pass a mock of inline type A
+            // into a method
+            // that accepts inline type B.
+            inlineClass.getDeclaredMethod("box-impl", underlyingType);
+            return true;
+        } catch (NoSuchMethodException e) {
+            return false;
+        }
+    }
+
+    private static Object unboxInlineClassIfPossible(Object boxedValue) {
+        Class<?> inlineClass = boxedValue.getClass();
+        try {
+            Method unboxImpl = inlineClass.getDeclaredMethod("unbox-impl");
+            return unboxImpl.invoke(boxedValue);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw Reporter.inlineClassWithoutUnboxImpl(inlineClass, e);
+        }
+    }
+
+    public static Object unboxUnderlyingValueIfNeeded(InvocationOnMock invocation, Object value) {
+        // Short path - Kotlin 1.5+ is not present.
+        if (value == null || jvmInlineAnnotation == null) {
+            return value;
+        }
+
+        Class<?> valueType = value.getClass();
+        InvocationInfo invocationInfo = new InvocationInfo(invocation);
+        Class<?> returnType = invocationInfo.getMethod().getReturnType();
+        if (valueType.isAssignableFrom(returnType)) return value;
+
+        if (isInlineClassWithAssignableUnderlyingType(valueType, returnType)) {
+            return unboxInlineClassIfPossible(value);
+        } else {
+            return value;
+        }
+    }
+}
diff --git a/subprojects/kotlinTest/kotlinTest.gradle b/subprojects/kotlinTest/kotlinTest.gradle
index 9e40101a52..4f39795584 100644
--- a/subprojects/kotlinTest/kotlinTest.gradle
+++ b/subprojects/kotlinTest/kotlinTest.gradle
@@ -1,6 +1,6 @@
 plugins {
     // Can not use libraries.kotlin.version here per https://stackoverflow.com/questions/37555196/in-gradle-how-to-use-a-variable-for-a-plugin-version
-    id 'org.jetbrains.kotlin.jvm' version '1.3.72'
+    id 'org.jetbrains.kotlin.jvm' version '1.5.0'
     id 'java'
 }
 


Test patch:
diff --git a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt
new file mode 100644
index 0000000000..a1a64ce0b0
--- /dev/null
+++ b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt
@@ -0,0 +1,342 @@
+package org.mockito.kotlin
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.mockito.Mockito.*
+
+@JvmInline
+value class IC(val i: Int)
+@JvmInline
+value class ICN(val i: Int?)
+@JvmInline
+value class SC(val s: String)
+@JvmInline
+value class SCN(val s: String?)
+
+class InlineClassTest {
+    interface I {
+        fun acceptsIC(ic: IC): Int
+        fun returnsIC(i: Int): IC
+
+        fun acceptsIC_N(ic: IC?): Int
+        fun returnsIC_N(i: Int): IC?
+
+        fun acceptsICN(ic: ICN): Int
+        fun returnsICN(i: Int): ICN
+
+        fun acceptsICN_N(ic: ICN?): Int
+        fun returnsICN_N(i: Int): ICN?
+
+        fun acceptsSC(ic: SC): Int
+        fun returnsSC(i: Int): SC
+
+        fun acceptsSC_N(ic: SC?): Int
+        fun returnsSC_N(i: Int): SC?
+
+        fun acceptsSCN(ic: SCN): Int
+        fun returnsSCN(i: Int): SCN
+
+        fun acceptsSCN_N(ic: SCN?): Int
+        fun returnsSCN_N(i: Int): SCN?
+    }
+
+    class Caller(val i: I) {
+        fun callAcceptsIC(ic: IC): Int = i.acceptsIC(ic)
+        fun callReturnsIC(int: Int): IC = i.returnsIC(int)
+
+        fun callAcceptsIC_N(ic: IC?): Int = i.acceptsIC_N(ic)
+        fun callReturnsIC_N(int: Int): IC? = i.returnsIC_N(int)
+
+        fun callAcceptsICN(ic: ICN): Int = i.acceptsICN(ic)
+        fun callReturnsICN(int: Int): ICN = i.returnsICN(int)
+
+        fun callAcceptsICN_N(ic: ICN?): Int = i.acceptsICN_N(ic)
+        fun callReturnsICN_N(int: Int): ICN? = i.returnsICN_N(int)
+
+        fun callAcceptsSC(ic: SC): Int = i.acceptsSC(ic)
+        fun callReturnsSC(int: Int): SC = i.returnsSC(int)
+
+        fun callAcceptsSC_N(ic: SC?): Int = i.acceptsSC_N(ic)
+        fun callReturnsSC_N(int: Int): SC? = i.returnsSC_N(int)
+
+        fun callAcceptsSCN(ic: SCN): Int = i.acceptsSCN(ic)
+        fun callReturnsSCN(int: Int): SCN = i.returnsSCN(int)
+
+        fun callAcceptsSCN_N(ic: SCN?): Int = i.acceptsSCN_N(ic)
+        fun callReturnsSCN_N(int: Int): SCN? = i.returnsSCN_N(int)
+    }
+
+    @Test
+    fun testAcceptsIC() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.acceptsIC(IC(1))).thenReturn(1)
+        `when`(mock.acceptsIC(IC(2))).thenReturn(2)
+
+        assertEquals(1, caller.callAcceptsIC(IC(1)))
+        assertEquals(2, caller.callAcceptsIC(IC(2)))
+
+        verify(mock).acceptsIC(IC(1))
+        verify(mock).acceptsIC(IC(2))
+    }
+
+    @Test
+    fun testReturnsIC() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.returnsIC(1)).thenReturn(IC(1))
+        `when`(mock.returnsIC(2)).thenReturn(IC(2))
+
+        assertEquals(IC(1), caller.callReturnsIC(1))
+        assertEquals(IC(2), caller.callReturnsIC(2))
+
+        verify(mock).returnsIC(1)
+        verify(mock).returnsIC(2)
+    }
+
+    @Test
+    fun testAcceptsIC_N() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.acceptsIC_N(IC(1))).thenReturn(1)
+        `when`(mock.acceptsIC_N(null)).thenReturn(0)
+
+        assertEquals(1, caller.callAcceptsIC_N(IC(1)))
+        assertEquals(0, caller.callAcceptsIC_N(null))
+
+        verify(mock).acceptsIC_N(IC(1))
+        verify(mock).acceptsIC_N(null)
+    }
+
+    @Test
+    fun testReturnsIC_N() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.returnsIC_N(1)).thenReturn(IC(1))
+        `when`(mock.returnsIC_N(0)).thenReturn(null)
+
+        assertEquals(IC(1), caller.callReturnsIC_N(1))
+        assertEquals(null, caller.callReturnsIC_N(0))
+
+        verify(mock).returnsIC_N(1)
+        verify(mock).returnsIC_N(0)
+    }
+
+    @Test
+    fun testAcceptsICN() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.acceptsICN(ICN(1))).thenReturn(1)
+        `when`(mock.acceptsICN(ICN(null))).thenReturn(0)
+
+        assertEquals(1, caller.callAcceptsICN(ICN(1)))
+        assertEquals(0, caller.callAcceptsICN(ICN(null)))
+
+        verify(mock).acceptsICN(ICN(1))
+        verify(mock).acceptsICN(ICN(null))
+    }
+
+    @Test
+    fun testReturnsICN() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.returnsICN(1)).thenReturn(ICN(1))
+        `when`(mock.returnsICN(0)).thenReturn(ICN(null))
+
+        assertEquals(ICN(1), caller.callReturnsICN(1))
+        assertEquals(ICN(null), caller.callReturnsICN(0))
+
+        verify(mock).returnsICN(1)
+        verify(mock).returnsICN(0)
+    }
+
+    @Test
+    fun testAcceptsICN_N() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.acceptsICN_N(ICN(1))).thenReturn(1)
+        `when`(mock.acceptsICN_N(null)).thenReturn(0)
+        `when`(mock.acceptsICN_N(ICN(null))).thenReturn(-1)
+
+        assertEquals(1, caller.callAcceptsICN_N(ICN(1)))
+        assertEquals(0, caller.callAcceptsICN_N(null))
+        assertEquals(-1, caller.callAcceptsICN_N(ICN(null)))
+
+        verify(mock).acceptsICN_N(ICN(1))
+        verify(mock).acceptsICN_N(null)
+        verify(mock).acceptsICN_N(ICN(null))
+    }
+
+    @Test
+    fun testReturnsICN_N() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.returnsICN_N(1)).thenReturn(ICN(1))
+        `when`(mock.returnsICN_N(0)).thenReturn(null)
+        `when`(mock.returnsICN_N(-1)).thenReturn(ICN(null))
+
+        assertEquals(ICN(1), caller.callReturnsICN_N(1))
+        assertEquals(null, caller.callReturnsICN_N(0))
+        assertEquals(ICN(null), caller.callReturnsICN_N(-1))
+
+        verify(mock).returnsICN_N(1)
+        verify(mock).returnsICN_N(0)
+        verify(mock).returnsICN_N(-1)
+    }
+
+    @Test
+    fun testAcceptsSC() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.acceptsSC(SC("1"))).thenReturn(1)
+        `when`(mock.acceptsSC(SC("2"))).thenReturn(2)
+
+        assertEquals(1, caller.callAcceptsSC(SC("1")))
+        assertEquals(2, caller.callAcceptsSC(SC("2")))
+
+        verify(mock).acceptsSC(SC("1"))
+        verify(mock).acceptsSC(SC("2"))
+    }
+
+    @Test
+    fun testReturnsSC() {
+        val mock = mock(I::class.java, withSettings().name("").defaultAnswer(RETURNS_MOCKS))
+        val caller = Caller(mock)
+
+        `when`(mock.returnsSC(1)).thenReturn(SC("1"))
+        `when`(mock.returnsSC(2)).thenReturn(SC("2"))
+
+        assertEquals(SC("1"), caller.callReturnsSC(1))
+        assertEquals(SC("2"), caller.callReturnsSC(2))
+
+        verify(mock).returnsSC(1)
+        verify(mock).returnsSC(2)
+    }
+
+    @Test
+    fun testAcceptsSC_N() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.acceptsSC_N(SC("1"))).thenReturn(1)
+        `when`(mock.acceptsSC_N(null)).thenReturn(0)
+
+        assertEquals(1, caller.callAcceptsSC_N(SC("1")))
+        assertEquals(0, caller.callAcceptsSC_N(null))
+
+        verify(mock).acceptsSC_N(SC("1"))
+        verify(mock).acceptsSC_N(null)
+    }
+
+    @Test
+    fun testReturnsSC_N() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.returnsSC_N(1)).thenReturn(SC("1"))
+        `when`(mock.returnsSC_N(0)).thenReturn(null)
+
+        assertEquals(SC("1"), caller.callReturnsSC_N(1))
+        assertEquals(null, caller.callReturnsSC_N(0))
+
+        verify(mock).returnsSC_N(1)
+        verify(mock).returnsSC_N(0)
+    }
+
+    @Test
+    fun testAcceptsSCN() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.acceptsSCN(SCN("1"))).thenReturn(1)
+        `when`(mock.acceptsSCN(SCN(null))).thenReturn(0)
+
+        assertEquals(1, caller.callAcceptsSCN(SCN("1")))
+        assertEquals(0, caller.callAcceptsSCN(SCN(null)))
+
+        verify(mock).acceptsSCN(SCN("1"))
+        verify(mock).acceptsSCN(SCN(null))
+    }
+
+    @Test
+    fun testReturnsSCN() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.returnsSCN(1)).thenReturn(SCN("1"))
+        `when`(mock.returnsSCN(0)).thenReturn(SCN(null))
+
+        assertEquals(SCN("1"), caller.callReturnsSCN(1))
+        assertEquals(SCN(null), caller.callReturnsSCN(0))
+
+        verify(mock).returnsSCN(1)
+        verify(mock).returnsSCN(0)
+    }
+
+    @Test
+    fun testAcceptsSCN_N() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.acceptsSCN_N(SCN("1"))).thenReturn(1)
+        `when`(mock.acceptsSCN_N(null)).thenReturn(0)
+        `when`(mock.acceptsSCN_N(SCN(null))).thenReturn(-1)
+
+        assertEquals(1, caller.callAcceptsSCN_N(SCN("1")))
+        assertEquals(0, caller.callAcceptsSCN_N(null))
+        assertEquals(-1, caller.callAcceptsSCN_N(SCN(null)))
+
+        verify(mock).acceptsSCN_N(SCN("1"))
+        verify(mock).acceptsSCN_N(null)
+        verify(mock).acceptsSCN_N(SCN(null))
+    }
+
+    @Test
+    fun testReturnsSCN_N() {
+        val mock = mock(I::class.java)
+        val caller = Caller(mock)
+
+        `when`(mock.returnsSCN_N(1)).thenReturn(SCN("1"))
+        `when`(mock.returnsSCN_N(0)).thenReturn(null)
+        `when`(mock.returnsSCN_N(-1)).thenReturn(SCN(null))
+
+        assertEquals(SCN("1"), caller.callReturnsSCN_N(1))
+        assertEquals(null, caller.callReturnsSCN_N(0))
+        assertEquals(SCN(null), caller.callReturnsSCN_N(-1))
+
+        verify(mock).returnsSCN_N(1)
+        verify(mock).returnsSCN_N(0)
+        verify(mock).returnsSCN_N(-1)
+    }
+
+    @Suppress("RESULT_CLASS_IN_RETURN_TYPE")
+    interface WithResult {
+        fun returnsResult(): Result<String>
+    }
+
+    @Suppress("RESULT_CLASS_IN_RETURN_TYPE")
+    class WithResultCaller(val i: WithResult) {
+        fun callReturnsResult(): Result<String> = i.returnsResult()
+    }
+
+    @Test
+    fun testResult() {
+        val mock = mock(WithResult::class.java)
+        val caller = WithResultCaller(mock)
+
+        `when`(mock.returnsResult()).thenReturn(Result.success("OK"))
+
+        assertEquals(Result.success("OK"), caller.callReturnsResult())
+
+        verify(mock).returnsResult()
+    }
+}