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

Stubbing fails for a method consuming value class with validation in constructor #911

Open
monosoul opened this issue Aug 29, 2022 · 2 comments

Comments

@monosoul
Copy link
Contributor

monosoul commented Aug 29, 2022

Expected Behavior

Stubbing a method consuming a value class with extra validation in the constructor doesn't fail.

Current Behavior

Stubbing fails with an exception (depends on the actual check in the constructor).

Failure Information (for bugs)

This started to happen in mockk 1.12.5, used to work as expected in 1.12.4.

Steps to Reproduce

Please provide detailed steps for reproducing the issue.

  1. Declare a value class with some validation in constructor;
  2. Try to stub a method consuming the value class using any() matcher;
  3. Get exception if the value generated for value class doesn't pass the validation.

Context

  • MockK version: 1.12.7
  • Kotlin version: 1.7.10
  • JDK version: 17
  • JUnit version: 5.9.0
  • Type of test: unit test

Stack trace

java.lang.IllegalArgumentException: 1343038854 is out of bounds

	at dev.monosoul.mockkissuereproducer.ValueClassWithValidationInConstructor.constructor-impl(ValueClassConsumerTest.kt:14)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97)
	at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Static.call(CallerImpl.kt:106)
	at kotlin.reflect.jvm.internal.calls.InlineClassAwareCaller.call(InlineClassAwareCaller.kt:142)
	at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108)
	at io.mockk.impl.recording.JvmSignatureValueGenerator.signatureValue(JvmSignatureValueGenerator.kt:24)
	at io.mockk.impl.recording.states.RecordingState.matcher(RecordingState.kt:46)
	at io.mockk.impl.recording.CommonCallRecorder.matcher(CommonCallRecorder.kt:52)
	at dev.monosoul.mockkissuereproducer.ValueClassConsumerTest$should not throw an exception when stubbing ValueClassConsumer$1.invoke(ValueClassConsumerTest.kt:28)
	at dev.monosoul.mockkissuereproducer.ValueClassConsumerTest$should not throw an exception when stubbing ValueClassConsumer$1.invoke(ValueClassConsumerTest.kt:23)
	at io.mockk.impl.eval.RecordedBlockEvaluator$record$block$1.invoke(RecordedBlockEvaluator.kt:25)
	at io.mockk.impl.eval.RecordedBlockEvaluator$enhanceWithRethrow$1.invoke(RecordedBlockEvaluator.kt:78)
	at io.mockk.impl.recording.JvmAutoHinter.autoHint(JvmAutoHinter.kt:23)
	at io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:40)
	at io.mockk.impl.eval.EveryBlockEvaluator.every(EveryBlockEvaluator.kt:30)
	at io.mockk.MockKDsl.internalEvery(API.kt:93)
	at io.mockk.MockKKt.every(MockK.kt:98)
	at dev.monosoul.mockkissuereproducer.ValueClassConsumerTest.should not throw an exception when stubbing ValueClassConsumer(ValueClassConsumerTest.kt:23)

Minimal reproducible code (the gist of this issue)

Full reproducer is available here: https://github.com/monosoul/mockk-issue-911-reproducer
And here's the same code, but with mockk 1.12.4: https://github.com/monosoul/mockk-issue-911-reproducer/tree/mockk-1.12.4

import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Test

class ValueClassConsumer {
    fun consume(instance: ValueClassWithValidationInConstructor) = "${instance.value}"
}

@JvmInline
value class ValueClassWithValidationInConstructor(val value: Int) {
    init {
        require(value in 1..10) { "$value is out of bounds" }
    }
}
class ValueClassConsumerTest {

    private val consumer = mockk<ValueClassConsumer>()

    @Test
    fun `should not throw an exception when stubbing ValueClassConsumer`() {
        every { consumer.consume(any()) } returns "something"
    }
}
@monosoul
Copy link
Contributor Author

I didn't dig deep into it, but seems like mockk generates a random value for ValueClassWithValidationInConstructor.value, therefore the test might pass sometimes when the value is within the bounds.

@monosoul monosoul changed the title any() matcher invokes actual value class constructor causing exceptions Stubbing fails for a method consuming value class with validation in constructor Aug 29, 2022
@aSemy
Copy link
Contributor

aSemy commented Aug 29, 2022

Good catch! Yes, at the moment the value classes don't have complete support.

In order to support them in #849 value classes are 'unwrapped' so the value inside can be mocked. The value class is then constructed as normal. In this case, this will trigger the validation.

I think this requires mocking value classes fully, although this isn't easy because of the mangling. A fix might help out #859.

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