我遵循MVP模式+ UseCases与Model层进行交互。这是我想测试的Presenter中的一种方法:
fun loadPreviews() {
launch(UI) {
val items = previewsUseCase.getPreviews() // a suspending function
println("[method] UseCase items: $items")
println("[method] View call")
view.showPreviews(items)
}
}
我的简单BDD测试:
fun <T> givenSuspended(block: suspend () -> T) = BDDMockito.given(runBlocking { block() })
infix fun <T> BDDMockito.BDDMyOngoingStubbing<T>.willReturn(block: () -> T) = willReturn(block())
@Test
fun `load previews`() {
// UseCase and View are mocked in a `setUp` method
val items = listOf<PreviewItem>()
givenSuspended { previewsUseCase.getPreviews() } willReturn { items }
println("[test] before Presenter call")
runBlocking { presenter.loadPreviews() }
println("[test] after Presenter call")
println("[test] verify the View")
verify(view).showPreviews(items)
}
测试成功通过,但日志中有些奇怪。我希望它是:
但事实证明是:
这种行为的原因是什么?我应该如何解决?
答案 0 :(得分:2)
我发现这是因为CoroutineDispatcher
。我曾经用UI
模仿EmptyCoroutineContext
上下文。切换到Unconfined
已解决了问题
答案 1 :(得分:2)
我看到你发现了自己,但我想为可能遇到同样问题的人解释一下
当您执行launch(UI) {}
时,会创建一个新的协程并将其分派到“UI”调度程序,这意味着您的协程现在在另一个线程上运行。
您的runBlocking{}
调用会创建一个新的协同程序,但runBlocking{}
将等待此协程在继续之前结束,您的loadPreviews()
函数会创建一个协程,启动它然后立即返回,所以runBlocking()
等待它并返回。
因此,当runBlocking{}
返回时,您使用launch(UI){}
创建的协程仍然在另一个线程中运行,这就是为什么日志的顺序搞砸了
Unconfined
上下文是一个特殊的CoroutineContext
,它只是创建一个在当前线程上执行协同程序的调度程序,所以现在当你执行runBlocking{}
时,它必须等待由launch{}
创建的协程结束,因为它在同一个线程上运行,从而阻塞了该线程。
我希望我的解释清楚,祝你有个美好的一天