为什么添加yield()会导致单元测试失败?

huangapple go评论73阅读模式
英文:

Why adding yeild() cause the unit test to fail?

问题

我可以帮你翻译成英文:

如何使用yield()方法测试可取消的协作函数?

我为这个存储库编写了一个单元测试,它通过了,但当我添加了`yield()`方法以使`refreshTitle`方法可以取消时,单元测试失败了。

 suspend fun refreshTitle() {
    withContext(ioDispatcher) {
        try {
            val result = network.fetchNextTitle()
            //yield()    //取消注释此行后,单元测试将失败。
            titleDao.insertTitle(Title(result))
        } catch (cause: Throwable) {
            throw TitleRefreshError("无法刷新标题", cause)
        }
    }
}

这是单元测试:

 @Test
fun whenRefreshTitleSuccess_insertsRows() = runBlockingTest() {
    val titleDao = TitleDaoFake("title")
    val subject = TitleRepository(
        network = MainNetworkFake("OK"),
        titleDao = titleDao,
        ioDispatcher = TestCoroutineDispatcher(),
    )
    subject.refreshTitle()
    Truth.assertThat(titleDao.nextInsertedOrNull()).isEqualTo("OK")
}

该代码来自于此Android CodeLab:https://developer.android.com/codelabs/kotlin-coroutines#9
英文:

How can I test a cooperative function for cancelation with the yeild() method?

I wrote a unit test for this repository and it passed but when I add yeild() method to make the refreshTitle method cooperative for cancellation then the unit test failed.

 suspend fun refreshTitle() {
    withContext(ioDispatcher) {
        try {
            val result = network.fetchNextTitle()
            //yield()    //by uncommenting this line the unit test fails.
            titleDao.insertTitle(Title(result))
        } catch (cause: Throwable) {
            throw TitleRefreshError("Unable to refresh title", cause)
        }
    }
}

This is the unit test:

 @Test
fun whenRefreshTitleSuccess_insertsRows() = runBlockingTest() {
    val titleDao = TitleDaoFake("title")
    val subject = TitleRepository(
        network = MainNetworkFake("OK"),
        titleDao = titleDao,
        ioDispatcher = TestCoroutineDispatcher(),
    )
    subject.refreshTitle()
    Truth.assertThat(titleDao.nextInsertedOrNull()).isEqualTo("OK")
}

The code comes from this android codelab: https://developer.android.com/codelabs/kotlin-coroutines#9

答案1

得分: 1

首先,我必须将这些库更新到 1.7.1 版本:

def coroutines_android_version = '1.7.1'
"org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_android_version",
"org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_android_version",
"org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_android_version"

为了防止出现此错误,我定义了一个名为 standardTestDispatcher 的字段,并使用相同的 TestCoroutineScheduler

Detected use of different schedulers. If you need to use several test coroutine dispatchers, create one TestCoroutineScheduler and pass it to each of them.

然后,我将测试函数更改如下:

private val scheduler = TestCoroutineScheduler()
private val standardTestDispatcher = StandardTestDispatcher(scheduler)

@Test
fun whenRefreshTitleSuccess_insertsRows() = runTest(
    context = standardTestDispatcher,
) {
    val titleDao = TitleDaoFake("title")
    val subject = TitleRepository(
        network = MainNetworkFake("OK"),
        titleDao = titleDao,
        ioDispatcher = standardTestDispatcher,
    )
    subject.refreshTitle()
    Truth.assertThat(titleDao.nextInsertedOrNull()).isEqualTo("OK")
}

现在,我可以成功测试带有 yield() 方法的协作函数以取消操作。它也可以在没有 yield() 的情况下工作:

suspend fun refreshTitle() {
    withContext(ioDispatcher) {
        try {
            val result = withTimeout(5_000) {
                network.fetchNextTitle()
            }
            yield()
            titleDao.insertTitle(Title(result))
        } catch (cause: Throwable) {
            throw TitleRefreshError("Unable to refresh title", cause)
        }
    }
}
英文:

First I have to update these libraries to 1.7.1 version:

def coroutines_android_version = '1.7.1'
"org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_android_version",
"org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_android_version",
"org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_android_version"

To prevent this error I defined StandardTestDispatcher with the same TestCroutineScheduler as a field called standardTestDispatcher.

Detected use of different schedulers. If you need to use several test coroutine dispatchers, create one TestCoroutineScheduler and pass it to each of them.

and then changed the test function as follows:

private val scheduler = TestCoroutineScheduler()
private val standardTestDispatcher = StandardTestDispatcher(scheduler)

@Test
fun whenRefreshTitleSuccess_insertsRows() = runTest(
    context = standardTestDispatcher,
) {
    val titleDao = TitleDaoFake("title")
    val subject = TitleRepository(
        network = MainNetworkFake("OK"),
        titleDao = titleDao,
        ioDispatcher = standardTestDispatcher,
    )
    subject.refreshTitle()
    Truth.assertThat(titleDao.nextInsertedOrNull()).isEqualTo("OK")
}

Now I can test the cooperative function for cancellation successfully with yeild() method. It works without yeild().

suspend fun refreshTitle() {
    withContext(ioDispatcher) {
        try {
            val result = withTimeout(5_000) {
                network.fetchNextTitle()
            }
            yield()
            titleDao.insertTitle(Title(result))
        } catch (cause: Throwable) {
            throw TitleRefreshError("Unable to refresh title", cause)
        }
    }
}

huangapple
  • 本文由 发表于 2023年7月13日 19:52:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76679072.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定