【问题标题】:Mockito's argThat returning null when in KotlinMockito 的 argThat 在 Kotlin 中返回 null
【发布时间】:2018-09-18 15:12:45
【问题描述】:

给定以下类(用 kotlin 编写):

class Target {
     fun <R> target(filter: String, mapper: (String) -> R): R = mapper(filter)
}

我可以在java中测试,测试代码:

@Test
public void testInJava() {
    Target mockTarget = Mockito.mock(Target.class);
    Mockito.when(mockTarget.target(
            argThat(it -> true),
            Mockito.argThat(it -> true)
    )).thenReturn(100);
    assert mockTarget.target("Hello World", it -> 1) == 100;
}

java 测试按预期通过,但同样的测试是用 kotlin 编写的:

@Test
fun test() {
    val mockTarget = Mockito.mock(Target::class.java)
    Mockito.`when`(mockTarget.target(
            Mockito.argThat<String> { true },
            mapper = Mockito.argThat<Function1<String, Int>>({ true }))
    ).thenReturn(100)
    assert(mockTarget.target("Hello World") { 1 } == 100)
}

kotlin 版本我收到如下异常:

java.lang.IllegalStateException: Mockito.argThat<String> { true } must not be null

为什么会发生这种情况以及如何使用 kotlin 进行测试?

【问题讨论】:

  • 显然可以为空的类型存在问题...请参见此处:medium.com/@elye.project/…
  • 谢谢@GiacomoAlzetta,你说得对,这是一个空类型的问题,我可以按照中等帖子解决。

标签: java kotlin mockito


【解决方案1】:

我也遇到了同样的问题。

最后,我发现argThat()会返回null,而且kotlin函数中的参数通常不接受null类型。

argThat的源代码来自ArgumentMatchers.java

public static <T> T argThat(ArgumentMatcher<T> matcher) {
    reportMatcher(matcher);
    return null;
}

你可以看到它return null。所以当我们mock这个函数的时候,它会抛出IllegalStateException,因为argThat返回null并且参数不能为null。

这意味着如果你的函数是:

fun doSomething(arg1: String): Int {
    // do something
}

当你这样嘲笑它时:

Mockito.`when`(
    doSomething(Mockito.argThat<String> { true })
).thenReturn(100)

它会抛出 IllegalStateException

所以你应该像这样改变你的功能:

fun doSomething(arg1: String?): Int {
    // do something
}

将“String”改为“String?”,使其接受空类型。

我的解决方案是用类定义参数?这样它可以接受null,但我不知道这是否是一个很好的解决方案

【讨论】:

  • 您好,谢谢您的回答,能否提供更多详细信息?
  • @ademar111190 简单地说你应该像这样改变你的函数:target(filter: String?, mapper: (String?) -> R)...然后它可以接受空类型参数跨度>
  • @user3120237 在我的测试中它不起作用。看,我确实上传了准备在 GitHub 上运行的问题代码:github.com/ademar111190/soq-test-mockito 如果您运行测试,您可以看到 java 通过并且 kotlin 失败,执行您建议的更改并没有解决问题。可以自己尝试一下。
  • @ademar111190 对于你的例子,也许你应该像fun &lt;R&gt; target(filter: String?, mapper: ((String?) -&gt; R)?): R? = mapper?.let { it(filter) }那样改变你的函数,很抱歉有一些错误,我无法运行。但是对于这个改变,它不会抛出 IllegalStateException
【解决方案2】:

2022 年,Mockito-Kotlin 正式解决了这个问题。

修复非常简单:只需从mockito-kotlin 包中导入argThat/eq/...,而不是mockito 包,一切就完成了!

相关:https://github.com/mockito/mockito-kotlin/wiki/Mocking-and-verifying

【讨论】:

  • 迟到总比不到好:D
  • 它不能解决我的问题。我正在使用mockito-kotlin 4.0.0,并且在使用argThat(导入org.mockito.kotlin.argThat)时仍然得到空指针。有什么我想念的吗?
  • @the_dark_destructor 可能会创建一个包含完整代码和错误消息的新 stackoverflow 问题
  • @ch271828n 解决了!如果您停止在argThat 中使用it,错误就会消失。所以,如果你使用像argThat { it.someValue == "value"} 这样的东西,你会得到一个空指针。如果你写argThat { someValue == "value"} 它可以工作......这两个写作不应该是等价的吗?如果是这样,我将打开一个新问题
  • @the_dark_destructor 请显示完整代码。我担心it 是否会在它之外捕获一些关闭?
【解决方案3】:

在撰写本文时,mockito-kotlin 已经一年多没有更新了。与所有这些库一样,始终需要使它们保持最新状态,我不想被无人维护的库所困。

所以我想出了另一种方法来解决 argThat 的 null 问题,而无需使用任何其他库。

假设我们有一个接口UuidRepository,如下所示:

interface UuidRepository {
    suspend fun Entity save(entity: Entity): Entity
}

Entity 类有两个属性,userId: Stringuuid: String

以下代码失败:

Mockito.verify(uuidRepository).save(argThat { it.userId == someValue && it.uuid == "test" })

出现错误:

argThat { it.userId == someValue && it.uuid == "test" } 不能为空

为了解决这个问题,我们在 mock 上获取所有调用,然后验证我们想要的:

val invocations = Mockito.mockingDetails(uuidRepository).invocations
    .filter { setOf("findById", "save").contains(it.method.name) }
    .map { it.method.name to it.arguments }
    .toMap()

assertThat(invocations).containsKey("save")
val first = invocations["save"]?.first()
assertThat(first).isNotNull
val entity = first as Entity
assertThat(entity.userId).isEqualTo(someValue)
assertThat(entity.uuid).isEqualTo("test")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-13
    • 1970-01-01
    • 2011-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多