【问题标题】:In kotlin, how do I mock a suspend function that wraps a callback?在 kotlin 中,我如何模拟一个包装回调的挂起函数?
【发布时间】:2018-08-03 13:16:14
【问题描述】:

假设有一个带有回调的接口:

interface SomeInterface {
    fun doSomething(arg: String, callback: (Exception?, Long) -> Unit)
}

我将其扩展为这样的挂起函数:

suspend fun SomeInterface.doSomething(arg: String): Long = suspendCoroutine { cont ->
    this.doSomething(arg) { err, result ->
        if (err == null) {
            cont.resume(result)
        } else {
            cont.resumeWithException(err)
        }
    }
}

我想在测试中模拟这个,但失败了。 理想情况下,我想使用这样的东西:

@Test
fun checkService() {
    runBlocking {
        val myService = mock<SomeInterface>()
        whenever(myService.doSomething(anyString())).thenReturn(1234L)
        val result = myService.doSomething("")
        assertEquals(result, 1234L)
    }
}

上面的语法因模拟异常而失败,因为它期待回调的匹配器。

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers!
2 matchers expected, 1 recorded:

如何模拟这样的挂起函数? 如果无法使用类似的语法,我该如何使用所需的参数进行模拟回调,以便在整个代码中使用的挂起变体在测试期间返回所需的结果?

更新: 当它是扩展功能时,它似乎是不可能的。根据Marko Topolnik 的评论,我认为这是因为扩展只是一个静态函数,超出了 mockito 的能力。

当挂起函数是成员函数时,它会按预期工作,使用我原来的语法。

这里有一些演示代码的要点: https://gist.github.com/mirceanis/716bf019a47826564fa57a77065f2335

【问题讨论】:

  • 它实际上并不期望回调的匹配器,而是所有suspend funs 在类文件级别声明的隐式延续参数。尝试为它提供一个空的Continuation
  • whenever(myService.doSomething(anyString(), any(Continuation.class))
  • 那不会编译,any&lt;Continuation&lt;Long&gt;&gt;() 也不会。另外,如果我使用any() 作为第二个参数,那么.thenReturn(1234L) 不起作用,因为它期待Unit,而不是Long 如果我在那里添加第二个参数,我基本上是在模拟原始接口调用,而不是挂起函数。
  • 我不介意嘲笑原始版本,只要挂起变体有效并在测试期间响应我想要的内容。我只是不知道如何实现。
  • 啊,是的,我没想到。扩展 fun 实际上是编译类中的public static Java 方法。你将无法嘲笑它。但是,有一些方法可以比 thenReturn 更复杂地模拟原件。

标签: callback kotlin mocking mockito kotlinx.coroutines


【解决方案1】:

我建议您使用MockK 进行测试,这样对协程更友好。

要模拟协程,您可以使用coEveryreturns,如下所示:

val interf = mockk<SomeInterface>()
coEvery { a.doSomething(any()) } returns Outcome.OK

【讨论】:

    【解决方案2】:

    当你需要时

    on .... 确实返回 ..

    方法被挂起,使用 mockito 你可以使用这个:

    • 使用这个库mockito-kotlin
    • 模拟您的对象,将其命名为 myObject(myObject 具有名为 isFoo 的挂起方法)

    然后:

     myObject.stub {
        onBlocking { isFoo() }.doReturn(true)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-05
      • 1970-01-01
      • 2018-06-16
      • 2021-12-21
      • 2013-11-17
      • 1970-01-01
      • 2020-03-13
      • 1970-01-01
      相关资源
      最近更新 更多