MainCoroutineRule 和 runBlocking 的区别

Difference between MainCoroutineRule and runBlocking

Kotlin 协程的

MainCoroutineRulerunBlocking 都是为测试目的而设计的。似乎两者都提供相同的功能:运行 在测试环境中同步编码。
那么有什么区别呢?它们各自的最佳用例是什么?

MainCoroutineRulerunBlocking看似相似实则有明显区别

MainCoroutineRule的定义:

MainCoroutineRule installs a TestCoroutineDispatcher for Disptachers.Main. Since it extends TestCoroutineScope, you can directly launch coroutines on the MainCoroutineRule as a [CoroutineScope]...

runBlocking的定义:

Runs a new coroutine and blocks the current thread interruptibly until its completion. This function should not be used from a coroutine. It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in main functions and in tests. ...

在定义上,runBlocking 仅用于协同程序中同步执行的目的。它不仅用于测试,还用于 UI 管理、数据管理和 Android 开发中的其他几个领域。

runBlocking 的使用更为普遍,而 MainCoroutineRule 仅在测试中使用,它具有与 runBlocking 相同的同步执行行为。但是,MainCoroutineRule 具有 runBlocking 中没有的更专业的测试功能,例如控制流管理。此外,使用 MainCoroutineRule 将使您的测试代码更清晰。

更详细地说,MainCoroutineRule 与 JUnit 规则直接相关。如果您以前使用过 JUnit,您可能还记得填写 @Before @After 为测试用例提供测试环境。如果你有多个测试用例,你最终将不得不编写多个 @Before @After 来提供多个测试环境,这可能会导致冗余的样板代码。现在,这就是 MainCoroutineRule 的亮点!使用 MainCoroutineRule,您可以声明一次测试环境,然后将它们重复用于多个测试用例。

比较下面两个示例代码:

不使用MainCoroutineRule

的测试用例
  @ExperimentalCoroutinesApi
    class MyViewModelTest {
        private val testDispatcher = TestCoroutineDispatcher()

        @Before
        fun setup() {
            Dispatchers.setMain(testDispatcher)
        }

        @After
        fun tearDown() {
            
            Dispatchers.resetMain()
         
            testDispatcher.cleanupTestCoroutines()
        }

        @Test
        fun testSomething() = runBlockingTest {
            ...
        }
    }

使用 MainCoroutineRule

的测试用例
@ExperimentalCoroutinesApi
class MainCoroutineRule(
    val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
) : TestWatcher() {

    override fun starting(description: Description?) {
        super.starting(description)
        Dispatchers.setMain(testDispatcher)
    }

    override fun finished(description: Description?) {
        super.finished(description)
        Dispatchers.resetMain()
        testDispatcher.cleanupTestCoroutines()
    }
}

@ExperimentalCoroutinesApi
fun MainCoroutineRule.runBlockingTest(block: suspend () -> Unit) =
    this.testDispatcher.runBlockingTest {
        block()
}

方便吗?这表明Google其实很关心开发者。

如果您有任何问题,请告诉我。