在 kotlin 中,如何模拟包装回调的挂起函数?

问题描述 投票:0回答:3

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

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

上面的语法失败并出现mockito异常,因为它需要回调的匹配器。

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

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

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

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

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

callback kotlin mocking mockito kotlinx.coroutines
3个回答
28
投票

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

要模拟协程,您可以使用

coEvery
returns
,如下所示:

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

27
投票

当你需要时

在..返回..

并且方法被暂停,通过 mockito 你可以使用这个:

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

然后:

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

0
投票

这是一个简单的解决方案: 创建一个实用函数并在模拟时调用它:

inline fun <reified T> anyNonNull(): T = Mockito.any(T::class.java)

然后你可以在代码中使用它,如下所示:

when(myService.doSomething(anyNonNull<String>())).thenReturn(1234L) 
© www.soinside.com 2019 - 2024. All rights reserved.