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

ClassCastException when mocking collection returns #609

Closed
LeonRa opened this issue Apr 27, 2021 · 4 comments
Closed

ClassCastException when mocking collection returns #609

LeonRa opened this issue Apr 27, 2021 · 4 comments
Labels

Comments

@LeonRa
Copy link
Contributor

LeonRa commented Apr 27, 2021

This is a regression seen when upgrading from MockK 1.10.0 to 1.11.0, though I have managed to trace it back to 1.10.4. This failure is observed when attempting to mock the return of a map (either templated or not) while mocking its wrapping class. For example:

class ClassWithMap(
    val map: Map<String, String>
)

@Test
fun `class with map fails mocking`() {
    val mock: ClassWithMap = mockk {
        every { map["foo"] } returns "bar"
    }

    mock.map["foo"] // Throws here after 1.10.4
}

There is a trivial work-around of creating a real map or a mock first and then passing that to the every block here, but I wanted to submit this in case it is a legitimate issue. A minimal reproducible code sample, including the above and a templated version which fails as well, can be seen below.

Failure Information

Context

  • MockK version: 1.10.0 - 1.11.0
  • OS: MacOS 10.15
  • Kotlin version: 1.4.32
  • JDK version: 1.8
  • JUnit version: 4.13
  • Type of test: Unit test

Failure Logs

Please include any relevant log snippets or files here.

Stack trace

// -----------------------[ YOUR STACK STARTS HERE ] -----------------------
WARNING: Failed to set backing field (skipping)
java.lang.IllegalArgumentException: Can not set final java.util.Map field TestClass$ClassWithMap.map to java.lang.String
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
	at sun.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:83)
	at java.lang.reflect.Field.set(Field.java:764)
	at io.mockk.InternalPlatformDsl.dynamicSetField(InternalPlatformDsl.kt:171)
	at io.mockk.impl.recording.states.StubbingAwaitingAnswerState.assignFieldIfMockingProperty(StubbingAwaitingAnswerState.kt:68)
	at io.mockk.impl.recording.states.StubbingAwaitingAnswerState.access$assignFieldIfMockingProperty(StubbingAwaitingAnswerState.kt:9)
	at io.mockk.impl.recording.states.StubbingAwaitingAnswerState$assignFieldIfMockingProperty$1.invoke(StubbingAwaitingAnswerState.kt:52)
	at io.mockk.impl.recording.states.StubbingAwaitingAnswerState$assignFieldIfMockingProperty$1.invoke(StubbingAwaitingAnswerState.kt:9)
	at io.mockk.impl.stub.AnswerAnsweringOpportunity.notifyFirstAnswerHandlers(AnswerAnsweringOpportunity.kt:30)
	at io.mockk.impl.stub.AnswerAnsweringOpportunity.provideAnswer(AnswerAnsweringOpportunity.kt:21)
	at io.mockk.MockKStubScope.answers(API.kt:2075)
	at io.mockk.MockKStubScope.returns(API.kt:2079)
	at TestClass.class with map fails mocking(TestClass.kt:18)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Map
	at TestClass$ClassWithMap.getMap(TestClass.kt:8)
	at TestClass.class with map fails mocking(TestClass.kt:21)
// -----------------------[ YOUR STACK TRACE ENDS HERE ] -----------------------

Minimal reproducible code

// -----------------------[ GRADLE DEFINITIONS ] -----------------------
dependencies {
  testImplementation 'io.mockk:mockk:1.10.4'
  testImplementation 'junit:junit:4.13'
}
// -----------------------[ YOUR CODE STARTS HERE ] -----------------------
import io.mockk.every
import io.mockk.mockk
import org.junit.Test

class TestClass {

    class ClassWithMap(
        val map: Map<String, String>
    )

    class TypedClassWithMap<T: Any>(
        val value: T
    )

    @Test
    fun `class with map fails mocking`() {
        val mock: ClassWithMap = mockk {
            every { map["foo"] } returns "bar"
        }

        mock.map["foo"]
    }

    @Test
    fun `typed class with map fails mocking`() {
        val mock: TypedClassWithMap<Map<String, String>> = mockk {
            every { value["foo"] } returns "bar"
        }

        mock.value["foo"]
    }
}
// -----------------------[ YOUR CODE ENDS HERE ] -----------------------
@Raibaz
Copy link
Collaborator

Raibaz commented Apr 27, 2021

Thanks for submitting this. It really looks like the same issue as #565 though.

It seems a bit weird for it to have been introduced in 1.10.4, as it was mostly a rollback release; my guess is that it's somewhat related to #536, which was released in 1.10.3 and dealt with collections being returned by the default answerer.

@LeonRa
Copy link
Contributor Author

LeonRa commented Apr 27, 2021

Ah, you're totally right about the duplicate. Sorry about that! I searched through the issues for ClassCastExceptions and must have missed it.

@stale
Copy link

stale bot commented Jun 28, 2021

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 Jun 28, 2021
@LeonRa
Copy link
Contributor Author

LeonRa commented Jun 28, 2021

Going to close this out as it's a confirmed duplicate of #565

@LeonRa LeonRa closed this as completed Jun 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants