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

any() doesn't work for value classes #847

Open
3 tasks done
maciejtulaza opened this issue Jul 14, 2022 · 6 comments · Fixed by #849
Open
3 tasks done

any() doesn't work for value classes #847

maciejtulaza opened this issue Jul 14, 2022 · 6 comments · Fixed by #849

Comments

@maciejtulaza
Copy link

maciejtulaza commented Jul 14, 2022

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 mock works and returns expected value from method

Current Behavior

Test fails with NullPointerException from io.mockk.impl.recording.states.RecordingState.matcher(RecordingState.kt:53)

Failure Information (for bugs)

After running the test where I use any() for value class I get NPE. I did not find any info about using value classes in MockK in your docs. However I've found similar issue reported in Mockito's GitHub: mockito/mockito-kotlin#445

Steps to Reproduce

Create such classes:

@JvmInline
value class Password(val pass: String)

interface SomeService {
    fun someMethod(password: Password): Int
}

class BasicSomeService : SomeService {
    override fun someMethod(password: Password): Int = 1234
}

and then one test:

class MockKFailTest : StringSpec({
    "mockk cant handle value classes" {
        val mock = mockk<SomeService>()
        every { mock.someMethod(any()) } returns 4321

        val resp = mock.someMethod(Password("qwerty"))

        resp shouldBe 4321
    }
})

Just run and you should get NPE.

Context

  • MockK version: 1.12.4
  • Kotest version: 5.3.0
  • OS: MacOS 11.6 BigSur
  • Kotlin version: 1.6.21
  • JDK version: 17.0.2-open
  • Type of test: unit test

Failure Logs

The output of such test is as follows:

java.lang.NullPointerException
at io.mockk.impl.recording.states.RecordingState.matcher(RecordingState.kt:53)
at io.mockk.impl.recording.CommonCallRecorder.matcher(CommonCallRecorder.kt:52)
@aSemy
Copy link
Contributor

aSemy commented Jul 20, 2022

See here: #152

There's a workaround in this comment: #152 (comment)

@maciejtulaza
Copy link
Author

Thanks @aSemy
Indeed part of this problem had been fixed by abovementioned issue

However, I still notice very strange behaviour of any() for value classes.

First problem:

Consider following setup:

@JvmInline
value class Foo(private val value: UUID)

@JvmInline
value class Bar(private val value: BigDecimal) {
    override fun toString(): String = value.toPlainString()
}

data class Wrapper(
    val bar: Bar,
)

class Service {
    fun method(wrapper: Wrapper, foo: Foo) {}
}

class AnyDoesntWork : StringSpec({
    "mocking fails on BigDecimal.toPlainString()" {
        val mock = mockk<Service>()

        verify(exactly = 0) { mock.method(any(), any()) }
    }
})

this will fail with an exception:

Cannot invoke "java.math.BigDecimal.toPlainString()" because "arg0" is null
java.lang.NullPointerException: Cannot invoke "java.math.BigDecimal.toPlainString()" because "arg0" is null
	at com.project.something.Bar.toString-impl(AnyDoesntWork.kt:16)
	at com.project.something.Wrapper.toString(AnyDoesntWork.kt)
	at java.base/java.lang.String.valueOf(String.java:4215)
	at java.base/java.lang.StringBuilder.append(StringBuilder.java:173)
	at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:457)
	at java.base/java.lang.String.valueOf(String.java:4215)
	at java.base/java.lang.StringBuilder.append(StringBuilder.java:173)

I don't know what exactly is causing this problem, but I noticed to get this exception you have to have:

  1. Two arguments in Service's method (order doesn't matter)
  2. One argument has to be a data class that contains property which is value class for BigDecimal (for non-data class another error is thrown: "Failed matching mocking signature for...")
  3. Second argument has to be a value class for type UUID

If you change any of these, the error will not appear.

Second problem:

Also, there is another problem I encountered. Trying to do simple verify {} does not work with value class, see:

class Service {
    fun method(foo: Foo) {}
}

class AnyDoesntWork : StringSpec({
    "mocking doesnt work" {
        val mock = mockk<Service>()

        verify(exactly = 0) { mock.method(any()) }
    }
})

this throws an exception:

Failed matching mocking signature for
SignedCall(retValue=, isRetValueMock=true, retType=class kotlin.Unit, self=Service(#1), method=method-X8XA9MQ(UUID), args=[00000000-0000-0000-0000-000000000000], invocationStr=Service(#1).method-X8XA9MQ(00000000-0000-0000-0000-000000000000))
left matchers: [any()]
io.mockk.MockKException: Failed matching mocking signature for
SignedCall(retValue=, isRetValueMock=true, retType=class kotlin.Unit, self=Service(#1), method=method-X8XA9MQ(UUID), args=[00000000-0000-0000-0000-000000000000], invocationStr=Service(#1).method-X8XA9MQ(00000000-0000-0000-0000-000000000000))
left matchers: [any()]
	at app//io.mockk.impl.recording.SignatureMatcherDetector.detect(SignatureMatcherDetector.kt:99)
	at app//io.mockk.impl.recording.states.RecordingState.signMatchers(RecordingState.kt:39)
	at app//io.mockk.impl.recording.states.RecordingState.round(RecordingState.kt:31)
	at app//io.mockk.impl.recording.CommonCallRecorder.round(CommonCallRecorder.kt:50)
	at app//io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:63)

I'm using Kotest 5.4.0 and MockK 1.12.5

@aSemy
Copy link
Contributor

aSemy commented Jul 30, 2022

Yeah - nested value classes aren't supported at present #859

@maciejtulaza
Copy link
Author

@aSemy right! and what about the second issue?

@qoomon
Copy link
Contributor

qoomon commented Jul 30, 2022

might be related to to my comment #849 (comment)

@stale
Copy link

stale bot commented Nov 2, 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants