question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Currently incompatible with inline class

See original GitHub issue

We want to experiment with inline classes in our application, but we are running into a compatibility issue with mockito-kotlin. I know inline classes are currently experimental and expected to change, so if it’s not worth the effort looking into at the moment I understand, I just want to start a conversation about it.

Issue

I can repro my issue with a unit test that looks like this:

interface Methods {
    ...
    fun inlineClass(): InlineClass
}

inline class InlineClass(val value: String)

class MockingTest : TestBase() {
    @Test
    fun testMockStubbing_inlineClass() {
        /* Given */
        val mock = mock<Methods>() {
            on { inlineClass() } doReturn InlineClass("A")
        }

        /* When */
        val result = mock.inlineClass()

        /* Then */
        expect(result).toBe(InlineClass("A"))
    } 
}

I get a stack trace that looks like this

java.lang.IllegalArgumentException: Parameter specified as non-null is null: method test.InlineClass.box-impl, parameter v

	at test.InlineClass.box-impl(Classes.kt)
	at test.MockingTest$testMockStubbing_inlineClass$mock$1$1.invoke(MockingTest.kt:19)
	at com.nhaarman.mockitokotlin2.KStubbing.on(KStubbing.kt:70)
	at test.MockingTest.testMockStubbing_inlineClass(MockingTest.kt:94)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

which points to where we call on { inlineClass() } or expect(result).

I tried to dig down with the debugger but couldn’t really get too far into what’s going on. I assume reflection is being used to create an instance of InlineClass but that has some core logic that an instance can’t be created without a value?

Has anyone seen this or have any ideas of how to go about fixing this? Or should we wait until inline classes mature a little bit?

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:19
  • Comments:9

github_iconTop GitHub Comments

5reactions
wemcommented, Jul 13, 2020

For me the following is working:

inline class InlineClass(val value: String)

fun matchesInlineClass(inlineClass: InlineClass) : InlineClass {
    return Mockito.argThat { arg: Any ->
        if (arg is String) {
            arg == inlineClass.value
        } else arg == inlineClass
    } as InlineClass? ?: inlineClass
}
4reactions
mkobitcommented, Mar 19, 2019

It looks like another option may be using Mockito’s Answer type.

The below is working for me with mockito-kotlin:2.1.0 and mockito-core:2.24.5

    @Test
    fun `Answer type implementation with IntInlineClass`() {
        /* Given */
        class IntInlineClassAnswer(private val value: Int) : Answer<Any> {
            override fun answer(invocation: InvocationOnMock?): Any = value
        }
        val mock = mock<Methods>() {
            on { intInlineClass() } doAnswer IntInlineClassAnswer(42)
        }

        assertEquals(IntInlineClass(42), mock.intInlineClass())
        // Can't use doAnswer to type constraints of mockito-kotlin `doAnswer {}`
        //whenever(mock.intInlineClass()).doAnswer { 50 }
        whenever(mock.intInlineClass()).thenAnswer { 50 }
        assertEquals(IntInlineClass(50), mock.intInlineClass())
    }

Answer itself seems like a bit of an odd feature, but it seems to work here.

One possible issue with the above is the doAnswer and thenAnswer being type constrained, when it doesn’t seem like the Answer API in Mockito really requires it?

infix fun <T> OngoingStubbing<T>.doAnswer(answer: Answer<*>): OngoingStubbing<T> {
    return thenAnswer(answer)
}

/**
 * Sets a generic Answer for the method using a lambda.
 */
infix fun <T> OngoingStubbing<T>.doAnswer(answer: (InvocationOnMock) -> T?): OngoingStubbing<T> {
    return thenAnswer(answer)
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Currently incompatible with inline class · Issue #309 - GitHub
We want to experiment with inline classes in our application, but we are running into a compatibility issue with mockito-kotlin.
Read more >
Using mockito-inline in project throws "Mockito cannot mock ...
I am using Mockito 2.23.4 and byte-buddy 1.9.3 in my project. I wanted to mock final classes and methods, so I used mockito-inline...
Read more >
disagree on InnerClasses attribute": cross-module inlined ...
The inlined copy's InnerClasses attribute lists the copy's outer class as the original's outer class, which causes an IncompatibleClassChangeError when trying ...
Read more >
class expression - JavaScript - MDN Web Docs - Mozilla
This is just a simple anonymous class expression which you can refer to using the ... If you want to refer to the...
Read more >
All About Kotlin Inline(Value) Class | Huawei Developers
This can lead to certain errors. For example, consider the two functions above. Here, there will be a conflict as the distance type...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found