测试无限Kotlin Coroutine
我有一个ViewModel。当它在屏幕上可见时,它就开始了。当用户离开屏幕时,它会停止。在启动ViewModel时,我想每5秒执行一些代码。该代码看起来有点像这样:
fun onStart() {
interval = launch(injectedDispatcher) {
while (true) {
doSomething()
delay(5000.milliseconds)
}
}
}
fun onStop() {
interval.cancel()
}
我想编写一个集成测试,该测试将与其依赖关系一起测试此视图模型。我使用TestScope即时进行此集成测试:
val scope = TestScope()
val injectedDispatcher = StandardTestDispatcher(scope.testScheduler)
@Test
fun interval() = scope.runTest {
val viewModel = get(injectedDispatcher)
viewModel.onStart()
delay(30000) // <- execution will get stuck at this point
assertSomething(...)
viewModel.onStop()
}
如果对测试的代码中没有无限循环,则此测试代码运行良好。但是,如果至少有一个无限的Coroutine,则延迟(30000)
将永远不会退出。取而代之的是,即使经过30000ms之后,执行将被卡在中,而(true)
循环。我还验证了scope.currenttime
可以超过30000ms的增加,而while loop仍然不会退出。
我认为这是因为StandardTestDisPatcher会在循环内部骑自行车,因为它一旦开始就无法暂停工作。
我做了一个小例子来说明问题: https://github.com/alexey-com/alexey-/alexey-/infinetetestest < /a>
是否有一种方法可以使用TestDisPatcher在特定时间内暂停无限循环?
I have a ViewModel. When it's visible on screen, it's started. When user leaves the screen, it stops. While the ViewModel is started, I want to execute some code every 5 seconds. The code looks somewhat like this:
fun onStart() {
interval = launch(injectedDispatcher) {
while (true) {
doSomething()
delay(5000.milliseconds)
}
}
}
fun onStop() {
interval.cancel()
}
I want to write an integration test that will test this ViewModel along with it's dependencies. I use TestScope to make this integration tests instant:
val scope = TestScope()
val injectedDispatcher = StandardTestDispatcher(scope.testScheduler)
@Test
fun interval() = scope.runTest {
val viewModel = get(injectedDispatcher)
viewModel.onStart()
delay(30000) // <- execution will get stuck at this point
assertSomething(...)
viewModel.onStop()
}
This testing code runs great if there are no infinite loops inside the code being tested. However, if there is at least one infinite coroutine, delay(30000)
will never exit. Instead, execuition will get stuck inside the while (true)
loop, even after 30000ms has passed. I've also verified that scope.currentTime
can be increased way over 30000ms and the while loop still won't quit.
I presume that this is because StandardTestDispatcher keeps cycling inside the while loop because it cannot suspend a job once it's started.
I've made a small example to illustrate the problem: https://github.com/Alexey-/InfiniteTest
Is there a way to suspend infinite loop after running it for a specific time with TestDispatcher?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
问题似乎是
testscope.runtest
将等待所有儿童coroutines在提供测试结果之前完成。执行不会陷入
延迟(30_000)
中。导致测试永远运行的原因是,您的断言失败并抛出了essertionError
。由于抛出了错误,因此从未调用下一行viewModel.onstop()
。这意味着您在ViewModel中启动的Coroutine永远不会完成,因此testscope.runtest
永远不会提供结果。您可以轻松测试:
最简单的解决方案是首先调用
viewmodel.onstop()
,然后运行您想要的任何断言。如果您关心一种完全替代方法,则可以避免完全手动开始和停止视图模式,然后选择更“ coroutine-y”的方法:
测试这可能会更像是这样:
并且在片段中使用了一个示例,这很容易适应活动或JetPack组成:
The problem appears to be that
TestScope.runTest
will wait for all child coroutines to complete before delivering test results.The execution does not get stuck in
delay(30_000)
. What causes the test to run forever is that your assertion fails and throws anAssertionError
. Because an Error was thrown, the next lineviewModel.onStop()
is never called. This means the coroutine launched in your ViewModel never completes and henceTestScope.runTest
will never deliver the result.You can test this easily:
The most simple solution would be to call
viewModel.onStop()
first, and then run whatever assertion you want to.If you care for a completely alternative approach, you could avoid starting and stopping your viewmodel by hand altogether, and opt for a more "coroutine-y" way:
Testing this would probably look more like this:
And an example usage in a Fragment, this can be easily adapted to an Activity or jetpack compose:
什么是StandardTestDisPatcher和TestScope?一个写了我的意见。所有工作。
测试看起来像。
模型看起来像是
这样的决定是当所有作业关闭时确定结果。当我们有断言错误时,我们会退出测试。范围正在工作,测试无法完成。
或者
What is the StandardTestDispatcher and TestScope? A wrote my opinion. All work.
Test looks like.
Model looks like
So the decision is assert results when all jobs is closed. When we have assert error we have break from test. Scope is working and test can't finish.
Or