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?