在 Android 应用中使用 Kotlin 协程 - 官方示例详解(4) - 直接测试协程

主要实现: 直接调用 suspend 函数的 测试用例.

1. 要测试的代码

TitleRepository.kt

suspend fun refreshTitle() {
   try {
       // Make network request using a blocking call
       val result = network.fetchNextTitle()
       titleDao.insertTitle(Title(result))
   } catch (cause: Throwable) {
       // If anything throws an exception, inform the caller
       throw TitleRefreshError("Unable to refresh title", cause)
   }
}

2. 测试用例

TitleRepositoryTest.kt

2.1 测试更新标题成功case ( refreshTitle)

@Test
fun whenRefreshTitleSuccess_insertsRows() {
   val subject = TitleRepository(
       MainNetworkFake("OK"),
       TitleDaoFake("title")
   )

   subject.refreshTitle()
}

如果写出上面这样, 是会有编译报错的。
因此 refreshTitle 是一个 suspend 函数,需要从 协程 或者 另外一个 suspend 函数调用.

但是,这里是测试程序,它不了解协程,因此无法设置为 suspend.
可以使用 CoroutineScope.launch 执行一个协程,不过它是异步代码 且 非阻塞,立即会返回执行后续代码.

因此, 可以使用 kotlinx-coroutines-test 库包含的 runBlockingTest 函数.
该函数会在调用 suspend 函数时 执行 阻塞.
可以将它看作一种将suspend函数和协程 转换为正常函数调用的方式

重要提示:runBlockingTest 函数将始终阻塞调用方,就像常规函数调用一样。
协程将在同一线程上同步运行。
您应避免在应用代码中使用 runBlocking 和 runBlockingTest,而应优先使用会立即返回的 launch。
runBlockingTest 只能在测试中使用,因为它是以测试控制的方式执行协程的,而 runBlocking 可用于为协程提供阻塞接口。

最后,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 将返回。

2.2 测试更新标题超时case ( refreshTitle)

向网络请求添加短暂超时

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

其中,
(1)此测试使用提供的虚构对象 MainNetworkCompletableFake,这是一个网络虚构对象,
用于暂停调用方,直到测试继续执行调用方为止。
当 refreshTitle 尝试发出网络请求时,它会永久挂起,因为我们想要测试超时情况。
(2) 然后,它会启动单独的协程来调用 refreshTitle。
这是测试超时的关键部分,发生超时的协程应与 runBlockingTest 创建的协程不同
(3)advanceTimeBy(5_000) 将时间调快 5s 并使另外一个 协程超时.

需要在 TitleRepository.refreshTitle 添加超时

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 throws an exception, inform the caller
       throw TitleRefreshError("Unable to refresh title", cause)
   }
}

你可能感兴趣的:(在 Android 应用中使用 Kotlin 协程 - 官方示例详解(4) - 直接测试协程)