我正在创建一个库,并且正在使用带有给我Deferred <>值的call-adapter的Retrofit。
在我代码中的函数中,我调用launch {}
,并在其中调用try-catch
值和可能的异常-调用不同的回调以获得不同的结果。
我在测试协程时发现的资源全部是关于测试挂起的函数的,而runBlocking {}
是所有问题的解决方案。除了我以外,不是
我做了一个简单的例子
@Mock
val mockListener: DoSomething.Listener = mock()
@Test
fun testSomething() {
val doer = DoSomething(mockListener)
runBlocking {
doer.doIt()
verify(mockListener).listen(any())
}
}
class DoSomething(val listener: Listener) {
interface Listener {
fun listen(s: String)
}
fun doIt() {
launch {
listener.listen(theThing().await())
}
}
private fun theThing(): Deferred<String> {
return async {
delay(5, TimeUnit.SECONDS)
return@async "Wow, a thing"
}
}
}
我想要的是实际运行的所有功能。该测试至少应花费5秒钟,但它只需要几毫秒即可完成代码。它不会阻塞。
我尝试添加
runBlocking {
launch {
// doer.doIt()
}.joinChildren()
}
和类似的做法,但是我只是无法让测试实际等待我在另一个类中的发布完成之前才完成测试。
将verify(...)
放在runBlocking
之外还会使测试失败,这应该这样做。
感谢任何投入,助手,良好实践等!
答案 0 :(得分:1)
您可以为const loadSomething = (action$) => Observable.forkJoin(
action$.ofType(actionTypes.LOAD_FIRST_SUCCESS).take(1),
action$.ofType(actionTypes.LOAD_SECOND_SUCCESS).take(1),
(firstList, secondList) =>
actions.loadSomethingSuccess({
firstList: firstList.payload,
secondList: secondList.payload
})
);
函数显式提供CoroutineContext:
doIt()
使用此参数,您可以轻松更改协程上下文-在测试代码中使用阻塞上下文:
fun doIt(context: CoroutineContext = DefaultDispatcher) {
launch(context) {
listener.listen(theThing().await()
}
}
顺便说一句:您不需要使用runBlocking {
doer.doIt(coroutineContext)
}
和launch
。使用async
,您将处于launch
上下文中,而无需异步运行suspendable
。特别是如果您在下一步中调用theThing()
:
await()
答案 1 :(得分:0)
最好的方法是不要像现在一样吞下Job
函数中的doIt()
。
代替
fun doIt() {
launch {
listener.listen(theThing().await())
}
}
做
fun doIt() = launch {
listener.listen(theThing().await())
}
这样,您的函数将返回协程,您可以等待:
doIt().join()
最好还是使用async()
代替launch()
另一条评论是,doIt()
实际上应该是doItAsync()
,如Kotlin准则所建议。