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

Implementation not mocked depending on constructor signature #1130

Open
3 tasks done
milgner opened this issue Aug 11, 2023 · 1 comment
Open
3 tasks done

Implementation not mocked depending on constructor signature #1130

milgner opened this issue Aug 11, 2023 · 1 comment

Comments

@milgner
Copy link
Contributor

milgner commented Aug 11, 2023

Prerequisites

Please answer the following questions for yourself before submitting an issue.

  • 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 following Kotest-based test should pass:

class MockkMePlease(val foo: NonEmptySet<Int>) {
    fun doStuff() {
        throw Exception("This should be mocked")
    }
}

class MockkInlineSpec : DescribeSpec({
    describe("NonEmptySet in constructor") {
        it("uses the mock") {
            val mock = mockk<MockkMePlease>()
            justRun { mock.doStuff() }
            mock.doStuff()
            verify { mock.doStuff() }
        }
    }
})

Current Behavior

All three references to doStuff - the blocks of justRun, verify as well as the call from the class under test - invoke the original implementation instead of the mockk-related behaviour.

Failure Information (for bugs)

Changing the constructor to use a regular Set instead of a NonEmptySet makes the implementation pass.

Steps to Reproduce

See above.

Context

Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.

  • MockK version: 1.13.5
  • OS: Linux
  • Kotlin version: 1.9.0 (tested with 1.8.20 as well)
  • JDK version: openjdk version "17.0.7" 2023-04-18
  • JUnit version: 5.9.2
  • Type of test: unit test (KOtest)

Failure Logs

Please include any relevant log snippets or files here.

Stack trace

java.lang.Exception: This should be mocked

Minimal reproducible code (the gist of this issue)

// -----------------------[ GRADLE DEFINITIONS ] -----------------------
dependencies {
    testImplementation("io.arrow-kt:arrow-core:1.2.0")
    testImplementation("io.kotest:kotest-assertions-core:5.6.2")
    testImplementation("io.kotest:kotest-framework-api-jvm:5.6.2")
}
// -----------------------[ YOUR CODE STARTS HERE ] -----------------------
package io.mockk.gh

import io.mockk.*
import io.kotest.core.spec.style.DescribeSpec
import arrow.core.NonEmptySet

class MockkMePlease(val foo: NonEmptySet<Int>) {
    fun doStuff() {
        throw Exception("This should be mocked")
    }
}

class MockkInlineSpec : DescribeSpec({
    describe("NonEmptySet in constructor") {
        it("uses the mock") {
            val mock = mockk<MockkMePlease>()
            justRun { mock.doStuff() }
            mock.doStuff()
            verify { mock.doStuff() }
        }
    }
})
// -----------------------[ YOUR CODE ENDS HERE ] -----------------------
@i-whammy
Copy link

i-whammy commented Aug 26, 2023

Hi @milgner , I investigated this issue and found some hypothesis.

As NonEmptyList is inlined and has a generic parameter, the type information is lost in compile.
The problem is comes from both inlined class and a generic parameter.

// Constructor of NonEmptyList
@JvmInline
public value class NonEmptySet<out A> private constructor(
  private val elements: Set<A>
) : Set<A> by elements, NonEmptyCollection<A> {
// minimal reproducible code
class Test : DescribeSpec({
    describe("This will fail") {
        val mock = mockk<DummyMockMePlease>()
        justRun { mock.foo() }
        mock.foo()
        verify { mock.foo() }
    }

    describe("Success when only inline class") {
        val mock = mockk<DummyMockMePlease2>()
        justRun { mock.foo() }
        mock.foo()
        verify { mock.foo() }
    }

    describe("Success when only generics") {
        val mock = mockk<DummyMockMePlease3>()
        justRun { mock.foo() }
        mock.foo()
        verify { mock.foo() }
    }
})

@JvmInline
value class DummyNonEmptySet<E>(val value: Set<E>)

class DummyMockMePlease(val arg0: DummyNonEmptySet<Int>) {
    fun foo() {
        throw Exception("This should be mocked.")
    }
}

@JvmInline
value class DummyNonEmptySet2(val value: Set<Any>)

class DummyMockMePlease2(val arg0: DummyNonEmptySet2) {
    fun foo() {
        throw Exception("This should be mocked.")
    }
}

class DummyNonEmptySet3 <E> (val value: Set<E>)

class DummyMockMePlease3(val arg0: DummyNonEmptySet3<Int>) {
    fun foo() {
        throw Exception("This should be mocked.")
    }
}

However, I feel unsure of my hypothesis and do not know how to solve this issue...

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