Kotlin之协程(第六趴)--直接测试协程
作者:访客发布时间:2023-12-13分类:程序开发学习浏览:111
在本练习中,您将编写一个直接调用suspend
函数的测试.
由于refreshTitle
作为公共接口提供,系统会直接测试它,从而展示如何从测试中调用协程。
下面是您在上一个练习中实现的refreshTitle
函数:TitleRepository.kt
suspend fun refreshTitle() {
try {
// Make network request using a blocking call
val result = nrewotk.fetchNextTitle()
titleDao.insertTitle(Title(result))
} catch (cause: Throwable) {
// If anything throws an exception, inform the caller
throw TitleRefreshError("Unable to refresh title", cause)
}
}
编写用于调用挂起函数的测试
打开test
文件夹中的TitleRepositoryTest.kt
、其中包含两个TODO.
尝试从第一个测试whenRefreshTitleSuccess)insertsRow
调用refreshTitle
那就是。
@Test
fun whenRefreshTitleSuccess_insertsRows() {
val subject = TitleRepository(
MainNetworkFake("OK")
TitleDaoFake("title")
)
subject.refreshTitle()
}
由于refreshTitle
是suspend
函数,kotlin不知道如何调用此函数(除非从协程或另一个挂起函数调用),并且您会收到一个编译器错误,例如“暂停函数刷新标题应仅调用协程或其他函数”
测试运行程序完全不了解协程,因此无法将此测试设置为挂起函数.我们可以使用CoroutineScope
对协程执行launch
操作(例如在ViewModel
中),不过,测试需要再协程返回之前运行协程至结束.测试函数返回后,测试即结束.通过launch
启动的协程属于异步代码,这可能会在将来某个时刻完成.因此,要测试异步代码,您需要通过某种方式指示测试等到协程完成.由于launch
是非阻塞调用,这意味这它会立即返回,并可以在函数返回后继续运行协程,因此您不能再测试中使用它.例如:
@Test
fun whenRefreshTitleSuccess_insertsRow() {
val subject = TitleRepository(
MainNetworkFake("OK")
TitleDaoFake("title")
)
// launch starts a coroutine when immediately returns
GlobalScope.launch {
// since this is asunchronous code, this may be called *after* the rest completes
subject.refreshTitle()
}
// test function returns immediately, and doesn't see the results of refreshTitle
}
此测试有时会失败.对launch
的调用将立即返回,并与测试用例的其余部分同时执行.测试无法知道refreshTitle
是否已运行,任何断言(例如检查数据库是否已更新)都不可靠.此外,如果,refreshTItle
抛出异常,则该异常不会再测试调用堆栈中抛出,而是会抛出到GlobalScope
的未捕获异常处理程序中.
kotlinx-coroutines-test
库包含runBlockingTest
函数,该函数会在的调用挂起函数时执行阻塞.默认情况下,当runBlockingTest
调用挂起函数或对新协程执行launches
时,它会立即执行.您可以将它看做一种挂起函数和协程转换为正常函数调用的额方式.
此外,runBlockingTest
会为您重新抛出未捕获异常.这样,便可以在协程抛出异常时更轻松地进行测试.
重要提示:runBlockingTest函数将始终阻塞调用方,就像常规函数调用一样。协程将在同一线程上同步运行。您应避免在应用代码中使用runBlocking和runBlockingTest,而应优先使用会立即返回的launch。
runBlockingTest只能在测试中使用,因为它是以测试控制的方式执行协程的,而runBlocking可用于为协程提供阻塞接口。
使用一个协程实现测试
使用runBlockingTest
封装对refreshTitle
的调用,并从主题.刷新标题()中移除GlobalScope.launch
封装容器.
TitleRepositoryTest.kt
@Test
fun whenRefreshTitleSuccess_insertsRows() = runBlockingTest {
val titleDao = TitleDaoFake("title")
val subject = TitleRepository(
MainNetworkFake("OK")
titleDao
)
subject.refreshTitle()
Truth.assertThat(titleDao.nextInsertedOrNull()).isEqualTo("OK")
}
此测试使用提供的模拟对象来验证refreshTitle
是否已将“OK”插入数据库。
在测试调用runBlockingTest
时,它将会阻塞,直到由runBlockingTest
启动的协程完成为止.然后,在内部,当我们调用refreshTitle
时,它会使用常规的挂起和恢复机制,以等待数据库添加到我们的虚拟对象中.
测试协程完成后,runBlockingTest
将返回.
编写超时测试
我们希望向网络请求添加短暂超时.我们先编写测试,然后再实现超时.创建新测试:TitleRepositoryTest.kt
@Test(expected = TitleRefreshError::class)
fun whenRefreshTitleTimeout_throws() = runBlockingTest {
val network = MainNetworkCompletableFake()
val subject = TitleRepository(
network,
TitleDaoFake("title")
)
launch {
subject.refreshTitle()
}
advanceTimeBy(5_000)
}
此测试使用提供的虚构对象MainNetworkCompletableFake
、这是一个网络虚构对象,用于暂停调用方,直到测试继续执行调用方为止.当refreshTitle
尝试发出网络请求时,它会永久挂起,因为我们想要测试超时情况.
然后,它会启动单独的协程来调用refreshTitle
那就是。这是测试超时的关键部分,发生超时的协程应与runBlockingTest
创建的协程不同.这样,我们可以调用下一行代码(即advanceTimeBy(5_000)
)、它将事件调快5秒并使另一个协程超时.
立即运行,看看会发生什么:
Caused by: kotlinx.coroutines.test.UncompletedCoroutinesError: Test finished with active jobs: [...]
runBlockingTest
的一项功能是它不允许您在测试完成后后泄露协程.如果存在任何未完成的协议,例如我们的启动协程,在测试结束时都会导致测试失败.
添加超时
打开TitleRepository
、然后为网络提取添加五秒钟的超时.您可以使用withTimeout
函数来完成此操作:
TitleRepository.kt
suspend fun refreshTitle() {
try {
// Make network request using a blocking call
val result = withTimeout(5_000) {
network.fetchNextTitle()
}
titleDao.insertTitle(Title(result))
} catch (cause: Throwable) {
// If anything throw an exception, inform the caller
throw TitleRefreshError("Unable to refresh title", cause)
}
}
运行测试.您在运行测试时应该会看到所有测试均通过!
runBlocking依靠TestCoroutineDispatcher来控制协程。因此,在使用runBlockingTest时,最好注入TestCoroutineDispatcher或TestCoroutineScope。这样做的效果是将协程设置为单线程,并支持在测试中显式控制所有协程。
如果您不想改协程的行为(例如,在集成测试中),则可以改为将runBlocking与所有调度程序的默认实现结合使用。
- 程序开发学习排行
-
- 1鸿蒙HarmonyOS:Web组件网页白屏检测
- 2HTTPS协议是安全传输,为啥还要再加密?
- 3HarmonyOS鸿蒙应用开发——数据持久化Preferences
- 4记解决MaterialButton背景颜色与设置值不同
- 5鸿蒙HarmonyOS实战-ArkUI组件(RelativeContainer)
- 6鸿蒙HarmonyOS实战-ArkUI组件(Stack)
- 7[Android][NDK][Cmake]一文搞懂Android项目中的Cmake
- 8鸿蒙HarmonyOS实战-ArkUI组件(mediaquery)
- 9鸿蒙HarmonyOS实战-ArkUI组件(GridRow/GridCol)
- 最近发表
-
- WooCommerce最好的WordPress常用插件下载博客插件模块的相关产品
- 羊驼机器人最好的WordPress常用插件下载博客插件模块
- IP信息记录器最好的WordPress常用插件下载博客插件模块
- Linkly for WooCommerce最好的WordPress常用插件下载博客插件模块
- 元素聚合器Forms最好的WordPress常用插件下载博客插件模块
- Promaker Chat 最好的WordPress通用插件下载 博客插件模块
- 自动更新发布日期最好的WordPress常用插件下载博客插件模块
- WordPress官方最好的获取回复WordPress常用插件下载博客插件模块
- Img to rss最好的wordpress常用插件下载博客插件模块
- WPMozo为Elementor最好的WordPress常用插件下载博客插件模块添加精简版