模拟静态 Java 和 Kotlin

huangapple go评论96阅读模式
英文:

Mock static java and kotlin

问题

我可以帮你翻译代码的部分。以下是你提供的代码的翻译:

在Java中,我有一个静态类,我想要进行模拟测试,我可以成功地做到这一点。但是当我转换到Kotlin时,测试现在失败,并显示以下错误:

检测到错误的参数匹配器放置或误用:->于TestTest.kt的TestTest.testWrapperStatic$lambda$0中

关于在Kotlin中模拟静态方法是否有什么不同的想法吗?

要模拟的类:

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

通过的Java测试用例:

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"));
    }
}

失败的Kotlin测试用例:

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"))
    }
}

请注意,代码中的"<"和"""字符是HTML编码,可能需要根据上下文进行调整。

英文:

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: -&gt; 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&lt;TextUtilsWrapper&gt; textUtilsWrapperMockedStatic = mockStatic(
                TextUtilsWrapper.class);
        textUtilsWrapperMockedStatic.when(
                () -&gt; TextUtilsWrapper.createFromParcel(any())).thenReturn(&quot;hello&quot;);

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

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`&lt;Any&gt; { createFromParcel(ArgumentMatchers.any()) }
            .thenReturn(&quot;hello&quot;)
        MatcherAssert.assertThat(createFromParcel(parcel), Matchers.`is`(&quot;hello&quot;))
    }
}

答案1

得分: 2

以下是翻译好的部分:

差异源于Kotlin添加了空值检查代码。
检查您的测试类的反编译源代码:

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());
  }
}

预期事件序列如下:

  • 调用参数匹配器并在堆栈上注册
  • 拦截模拟的方法调用,并调用MockMethodAdvice.handleStatic
  • 这将调用ArgumentMatcherStorageImpl.pullLocalizedMatchers并清除匹配器堆栈。

请参阅:https://stackoverflow.com/questions/22822512/how-do-mockito-matchers-work

不幸的是,Intrinsics.checkNotNullExpressionValue引发了NullPointerExeption,因为ArgumentMatchers.any()返回null。

这个NPE被静默忽略,但模拟的方法没有被调用,因此没有调用handleStatic - 匹配器堆栈未被清除。

MockedStaticImpl.when调用时检查匹配器堆栈,并报告InvalidUseOfMatchersException

要解决此问题,可以使用mockito-kotlin提供的参数匹配器:

/** 匹配任何对象,但不包括null。 */
inline fun <reified T : Any> any(): T {
    return ArgumentMatchers.any(T::class.java) ?: createInstance()
}

/** 匹配任何内容,包括null。 */
inline fun <reified T : Any> anyOrNull(): T {
    return ArgumentMatchers.any<T>() ?: createInstance()
}

希望这些信息对您有所帮助。

英文:

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(&quot;hello&quot;);
    Intrinsics.checkNotNullExpressionValue(parcel, &quot;parcel&quot;);
    Assertions.assertEquals(&quot;hello&quot;, TextUtilsWrapper.createFromParcel(parcel));
  }
  
  private static final void testWrapperStatic$lambda$0() {
    Intrinsics.checkNotNullExpressionValue(ArgumentMatchers.any(), &quot;any()&quot;);
    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: https://stackoverflow.com/questions/22822512/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 &lt;reified T : Any&gt; any(): T {
    return ArgumentMatchers.any(T::class.java) ?: createInstance()
}

/** Matches anything, including nulls. */
inline fun &lt;reified T : Any&gt; anyOrNull(): T {
    return ArgumentMatchers.any&lt;T&gt;() ?: createInstance()
}

huangapple
  • 本文由 发表于 2023年6月12日 13:42:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76453879.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定