Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected result unwrapping when mocking a suspending function with default arguments that returns kotlin.Result #777

Closed
3 tasks done
KacperKluka opened this issue Jan 3, 2022 · 5 comments

Comments

@KacperKluka
Copy link

Prerequisites

  • I am running the latest version
  • I checked the documentation and found no answer
  • I checked to make sure that this issue has not already been filed

Expected Behavior

The mocked suspending function with default arguments returns the Result<T>.

Current Behavior

The mocked function returns T instead of Result<T> and throws a java.lang.ClassCastException.

Failure Information (for bugs)

The unwrapping of the Result only happens if we use the default arguments and omit some of them when calling the function. If we specify all the arguments when calling the function then the mock behaves correctly and returns the Result without unwrapping. Additionally, this only happens when the mocked function is a suspend fun, for regular ones it works as expected.

Steps to Reproduce

First, we'll need an interface with a method that is a suspend fun with default arguments. This method has to return a value wrapped in a Result, here we'll use Result<Unit>. Below is an interface with a method that has one required argument name and one optional, default argument optionalFlag.

    interface MyInterface {
        suspend fun getResult(name: String, optionalFlag: Boolean = true): Result<Unit>
    }

Then, we need to mock the method to always return Result.success(Unit). Just in case we're mocking both versions of the method: with and without the default argument. Then we try to call the method and verify that the result matches the one we've provided to the mock.

    @Test
    fun shouldReturnResult() {
        runBlocking {
            // given
            val expectedResult = Result.success(Unit)
            val myInterface = mockk<MyInterface> {
                coEvery { getResult(any()) } returns expectedResult
                coEvery { getResult(any(), any()) } returns expectedResult
            }

            // when
            val result = myInterface.getResult("name")

            // then
            Assert.assertEquals(expectedResult, result)
        }
    }

After that instead of receiving a Result<Unit> from the myInterface.getResult("name") call we receive a ClassCastException because the method returns a Unit instead of a Result<Unit>.

java.lang.ClassCastException: class kotlin.Unit cannot be cast to class kotlin.Result (kotlin.Unit and kotlin.Result are in unnamed module of loader 'app')

Context

  • MockK version: 1.12.2
  • OS: Ubuntu 20.04.3 LTS
  • Kotlin version: 1.5.10
  • JDK version: 11.0.13
  • JUnit version: 4.13.1
  • Type of test: unit test

Minimal reproducible code (the gist of this issue)

// -----------------------[ GRADLE DEFINITIONS ] -----------------------
dependencies {
    testImplementation 'io.mockk:mockk:1.12.2'
    testImplementation 'junit:junit:4.13.1'
}
// -----------------------[ YOUR CODE STARTS HERE ] -----------------------
import io.mockk.coEvery
import io.mockk.mockk
import org.junit.Assert
import org.junit.Test

class MyTest {

    interface MyInterface {
        suspend fun getResult(name: String, optionalFlag: Boolean = true): Result<Unit>
    }

    @Test
    fun shouldReturnResult() {
        runBlocking {
            // given
            val expectedResult = Result.success(Unit)
            val myInterface = mockk<MyInterface> {
                coEvery { getResult(any()) } returns expectedResult
                coEvery { getResult(any(), any()) } returns expectedResult
            }

            // when
            val result = myInterface.getResult("name")

            // then
            Assert.assertEquals(expectedResult, result)
        }
    }
}
// -----------------------[ YOUR CODE ENDS HERE ] -----------------------
@Raibaz
Copy link
Collaborator

Raibaz commented Jan 3, 2022

This really looks like a duplicate of #645.

@KacperKluka
Copy link
Author

It is connected to that issue for sure, but I'm not certain if those two issues are exactly the same 🤔

@stale
Copy link

stale bot commented Apr 16, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. If you are sure that this issue is important and should not be marked as stale just ask to put an important label.

@stale stale bot added the stale label Apr 16, 2022
@KacperKluka
Copy link
Author

FYI I've just checked and after upgrading the Kotlin version from 1.5.10 to 1.6.10 the problem is gone. So I guess that this issue can be closed 😉

@stale stale bot removed the stale label Apr 18, 2022
@Raibaz
Copy link
Collaborator

Raibaz commented Apr 18, 2022

Cool, thanks for sharing this!

@Raibaz Raibaz closed this as completed Apr 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants