模拟静态 Java 和 Kotlin

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

Mock static java and kotlin

问题

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

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

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

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

要模拟的类:

  1. object TextUtilsWrapper {
  2. @JvmStatic
  3. fun createFromParcel(p: Parcel): CharSequence {
  4. return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p)
  5. }
  6. }

通过的Java测试用例:

  1. public class TestTest extends BaseMockingUnitTest {
  2. @Test
  3. public void testWrapperStatic() {
  4. Parcel parcel = Mockito.mock(Parcel.class);
  5. final MockedStatic<TextUtilsWrapper> textUtilsWrapperMockedStatic = mockStatic(
  6. TextUtilsWrapper.class);
  7. textUtilsWrapperMockedStatic.when(
  8. () -> TextUtilsWrapper.createFromParcel(any())).thenReturn("hello");
  9. assertThat(TextUtilsWrapper.createFromParcel(parcel), is("hello"));
  10. }
  11. }

失败的Kotlin测试用例:

  1. class TestTest : BaseMockingUnitTest() {
  2. @Test
  3. fun testWrapperStatic() {
  4. val parcel = Mockito.mock(Parcel::class.java)
  5. val textUtilsWrapperMockedStatic = Mockito.mockStatic(
  6. TextUtilsWrapper::class.java
  7. )
  8. textUtilsWrapperMockedStatic.`when`<Any> { createFromParcel(ArgumentMatchers.any()) }
  9. .thenReturn("hello")
  10. MatcherAssert.assertThat(createFromParcel(parcel), Matchers.`is`("hello"))
  11. }
  12. }

请注意,代码中的"<"和"""字符是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

  1. 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,

  1. object TextUtilsWrapper {
  2. @JvmStatic
  3. fun createFromParcel(p: Parcel): CharSequence {
  4. return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p)
  5. }
  6. }

Passing Java test case,

  1. public class TestTest extends BaseMockingUnitTest {
  2. @Test
  3. public void testWrapperStatic() {
  4. Parcel parcel = Mockito.mock(Parcel.class);
  5. final MockedStatic&lt;TextUtilsWrapper&gt; textUtilsWrapperMockedStatic = mockStatic(
  6. TextUtilsWrapper.class);
  7. textUtilsWrapperMockedStatic.when(
  8. () -&gt; TextUtilsWrapper.createFromParcel(any())).thenReturn(&quot;hello&quot;);
  9. assertThat(TextUtilsWrapper.createFromParcel(parcel), is(&quot;hello&quot;));
  10. }
  11. }

Failing kotlin test case,

  1. class TestTest : BaseMockingUnitTest() {
  2. @Test
  3. fun testWrapperStatic() {
  4. val parcel = Mockito.mock(Parcel::class.java)
  5. val textUtilsWrapperMockedStatic = Mockito.mockStatic(
  6. TextUtilsWrapper::class.java
  7. )
  8. textUtilsWrapperMockedStatic.`when`&lt;Any&gt; { createFromParcel(ArgumentMatchers.any()) }
  9. .thenReturn(&quot;hello&quot;)
  10. MatcherAssert.assertThat(createFromParcel(parcel), Matchers.`is`(&quot;hello&quot;))
  11. }
  12. }

答案1

得分: 2

以下是翻译好的部分:

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

  1. public final class TextUtilsWrapperTest {
  2. @Test
  3. public final void testWrapperStatic() {
  4. Parcel parcel = (Parcel) Mockito.mock(Parcel.class);
  5. MockedStatic textUtilsWrapperMockedStatic = Mockito.mockStatic(TextUtilsWrapper.class);
  6. textUtilsWrapperMockedStatic.when(TextUtilsWrapperTest::testWrapperStatic$lambda$0)
  7. .thenReturn("hello");
  8. Intrinsics.checkNotNullExpressionValue(parcel, "parcel");
  9. Assertions.assertEquals("hello", TextUtilsWrapper.createFromParcel(parcel));
  10. }
  11. private static final void testWrapperStatic$lambda$0() {
  12. Intrinsics.checkNotNullExpressionValue(ArgumentMatchers.any(), "any()");
  13. TextUtilsWrapper.createFromParcel((Parcel) ArgumentMatchers.any());
  14. }
  15. }

预期事件序列如下:

  • 调用参数匹配器并在堆栈上注册
  • 拦截模拟的方法调用,并调用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提供的参数匹配器:

  1. /** 匹配任何对象,但不包括null。 */
  2. inline fun <reified T : Any> any(): T {
  3. return ArgumentMatchers.any(T::class.java) ?: createInstance()
  4. }
  5. /** 匹配任何内容,包括null。 */
  6. inline fun <reified T : Any> anyOrNull(): T {
  7. return ArgumentMatchers.any<T>() ?: createInstance()
  8. }

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

英文:

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

  1. public final class TextUtilsWrapperTest {
  2. @Test
  3. public final void testWrapperStatic() {
  4. Parcel parcel = (Parcel)Mockito.mock(Parcel.class);
  5. MockedStatic textUtilsWrapperMockedStatic = Mockito.mockStatic(TextUtilsWrapper.class);
  6. textUtilsWrapperMockedStatic.when(TextUtilsWrapperTest::testWrapperStatic$lambda$0)
  7. .thenReturn(&quot;hello&quot;);
  8. Intrinsics.checkNotNullExpressionValue(parcel, &quot;parcel&quot;);
  9. Assertions.assertEquals(&quot;hello&quot;, TextUtilsWrapper.createFromParcel(parcel));
  10. }
  11. private static final void testWrapperStatic$lambda$0() {
  12. Intrinsics.checkNotNullExpressionValue(ArgumentMatchers.any(), &quot;any()&quot;);
  13. TextUtilsWrapper.createFromParcel((Parcel)ArgumentMatchers.any());
  14. }
  15. }

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:

  1. /** Matches any object, excluding nulls. */
  2. inline fun &lt;reified T : Any&gt; any(): T {
  3. return ArgumentMatchers.any(T::class.java) ?: createInstance()
  4. }
  5. /** Matches anything, including nulls. */
  6. inline fun &lt;reified T : Any&gt; anyOrNull(): T {
  7. return ArgumentMatchers.any&lt;T&gt;() ?: createInstance()
  8. }

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:

确定