作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我遵循 MVP 模式 + UseCases 与模型层进行交互。这是我要测试的 Presenter 中的一种方法:
fun loadPreviews() {
launch(UI) {
val items = previewsUseCase.getPreviews() // a suspending function
println("[method] UseCase items: $items")
println("[method] View call")
view.showPreviews(items)
}
}
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)
}
最佳答案
我发现这是因为 CoroutineDispatcher
.我曾经 mock UI
EmptyCoroutineContext
的上下文.切换到 Unconfined
解决了问题
更新 02.04.20
问题的名称表明将有一个详尽的解释如何对挂起函数进行单元测试。所以让我再解释一下。
测试挂起函数的主要问题是线程。假设我们要测试这个在不同线程中更新属性值的简单函数:
class ItemUpdater(val item: Item) {
fun updateItemValue() {
launch(Dispatchers.Default) { item.value = 42 }
}
}
Dispatchers.Default
与另一个调度员一起仅用于测试目的。我们有两种方法可以做到这一点。每种都有其优点和缺点,选择哪一种取决于您的项目和编码风格:
class ItemUpdater(
val item: Item,
val dispatcher: CoroutineDispatcher // can be a wrapper that provides multiple dispatchers but let's keep it simple
) {
fun updateItemValue() {
launch(dispatcher) { item.value = 42 }
}
}
// later in a test class
@Test
fun `item value is updated`() = runBlocking {
val item = Item()
val testDispatcher = Dispatchers.Unconfined // can be a TestCoroutineDispatcher but we still keep it simple
val updater = ItemUpdater(item, testDispatcher)
updater.updateItemValue()
assertEquals(42, item.value)
}
class ItemUpdater(val item: Item) {
fun updateItemValue() {
launch(DispatchersProvider.Default) { item.value = 42 } // DispatchersProvider is our own global wrapper
}
}
// later in a test class
// -----------------------------------------------------------------------------------
// --- This block can be extracted into a JUnit Rule and replaced by a single line ---
// -----------------------------------------------------------------------------------
@Before
fun setUp() {
DispatchersProvider.Default = Dispatchers.Unconfined
}
@After
fun cleanUp() {
DispatchersProvider.Default = Dispatchers.Default
}
// -----------------------------------------------------------------------------------
@Test
fun `item value is updated`() = runBlocking {
val item = Item()
val updater = ItemUpdater(item)
updater.updateItemValue()
assertEquals(42, item.value)
}
Dispatchers.Default
。在测试课上。唯一的区别是他们如何做到这一点。选择哪一个真的完全取决于你,所以不要被我自己的想法所左右。
DispatchersWrapper
。仅用于测试目的。然而谷歌
recommends this way至少现在。第二种风格使事情变得简单,并且不会使生产类复杂化。这就像 RxJava 的测试方式,您必须通过 RxJavaPlugins 替换调度程序。顺便说一句,
kotlinx-coroutines-test
will bring the exact same functionality future 的某一天。
关于unit-testing - 如何对 Kotlin 挂起函数进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49865054/
我是一名优秀的程序员,十分优秀!