0

I have a static class that I want to mock, I can do this successfully in java. But when I convert to kotlin and tests now fail with the error

Misplaced or misused argument matcher detected here: -> at TestTest.testWrapperStatic$lambda$0(TestTest.kt:19)

Any ideas on what is different when mocking static in kotlin?

Class to mock,

object TextUtilsWrapper {
    @JvmStatic
    fun createFromParcel(p: Parcel): CharSequence {
        return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p)
    }
}

Passing Java test case,

public class TestTest extends BaseMockingUnitTest {

    @Test
    public void testWrapperStatic() {
        Parcel parcel = Mockito.mock(Parcel.class);

        final MockedStatic<TextUtilsWrapper> textUtilsWrapperMockedStatic = mockStatic(
                TextUtilsWrapper.class);
        textUtilsWrapperMockedStatic.when(
                () -> TextUtilsWrapper.createFromParcel(any())).thenReturn("hello");

        assertThat(TextUtilsWrapper.createFromParcel(parcel), is("hello"));
    }
}

Failing kotlin test case,

class TestTest : BaseMockingUnitTest() {
    @Test
    fun testWrapperStatic() {
        val parcel = Mockito.mock(Parcel::class.java)
        val textUtilsWrapperMockedStatic = Mockito.mockStatic(
            TextUtilsWrapper::class.java
        )
        textUtilsWrapperMockedStatic.`when`<Any> { createFromParcel(ArgumentMatchers.any()) }
            .thenReturn("hello")
        MatcherAssert.assertThat(createFromParcel(parcel), Matchers.`is`("hello"))
    }
}
fergdev
  • 963
  • 1
  • 13
  • 27

1 Answers1

2

The difference stems from the fact that Kotlin adds null-checking code. Check the decompiled source of your test class:

public final class TextUtilsWrapperTest {
  @Test
  public final void testWrapperStatic() {
    Parcel parcel = (Parcel)Mockito.mock(Parcel.class);
    MockedStatic textUtilsWrapperMockedStatic = Mockito.mockStatic(TextUtilsWrapper.class);
    textUtilsWrapperMockedStatic.when(TextUtilsWrapperTest::testWrapperStatic$lambda$0)
      .thenReturn("hello");
    Intrinsics.checkNotNullExpressionValue(parcel, "parcel");
    Assertions.assertEquals("hello", TextUtilsWrapper.createFromParcel(parcel));
  }
  
  private static final void testWrapperStatic$lambda$0() {
    Intrinsics.checkNotNullExpressionValue(ArgumentMatchers.any(), "any()");
    TextUtilsWrapper.createFromParcel((Parcel)ArgumentMatchers.any());
  }
}

The expected event sequence is:

  • argument matcher is called and registered on stack
  • mocked method call is intercepted and MockMethodAdvice.handleStatic is called
  • this calls ArgumentMatcherStorageImpl.pullLocalizedMatchers and clears the matchers stack.

See: How do Mockito matchers work?

Unfortunately, Intrinsics.checkNotNullExpressionValue throws a NullPointerExeption, as ArgumentMatchers.any() returnes null.

This NPE is silently ignored, but mocked method is not called, and thus handleStatic is not called - the matchers stack is not cleared.

The matchers stack is checked upon MockedStaticImpl.when call, and the InvalidUseOfMatchersException is reported.

To solve, use mockito-kotlin provided argument matchers:

/** Matches any object, excluding nulls. */
inline fun <reified T : Any> any(): T {
    return ArgumentMatchers.any(T::class.java) ?: createInstance()
}

/** Matches anything, including nulls. */
inline fun <reified T : Any> anyOrNull(): T {
    return ArgumentMatchers.any<T>() ?: createInstance()
}
Lesiak
  • 22,088
  • 2
  • 41
  • 65