AdvanceTimeBy has no effect within test block when using runBlockingTest
See original GitHub issuePretty simple repro with the following test:
@Test
fun test() = runBlockingTest {
val flow = flow {
delay(100L)
emit("a")
}
flow.test {
advanceTimeBy(101L)
assertThat(expectItem()).isEqualTo("a")
}
}
However, the test above fails with the following error:
kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1000 ms
at kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:186)
at kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:156)
at kotlinx.coroutines.test.TimedRunnable.run(TestCoroutineDispatcher.kt)
at kotlinx.coroutines.test.TestCoroutineDispatcher.doActionsUntil(TestCoroutineDispatcher.kt:103)
at kotlinx.coroutines.test.TestCoroutineDispatcher.advanceUntilTime(TestCoroutineDispatcher.kt:123)
at kotlinx.coroutines.test.TestCoroutineDispatcher.advanceUntilIdle(TestCoroutineDispatcher.kt:133)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:52)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest$default(TestBuilders.kt:45)
at com.repro.ReproTest.test(ReproTest.kt:16)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
I suspect this is probably some nuance with TestCoroutineDispatcher
plus the fact that test { … }
is internally using Dispatchers.Unconfined
.
Kotlin: 1.4.31 Coroutines: 1.4.3 Turbine: 0.4.1
Issue Analytics
- State:
- Created 2 years ago
- Reactions:10
- Comments:7 (3 by maintainers)
Top Results From Across the Web
Kotlin - Test inner Coroutines with delay using runBlockingTest
I know I can test code using delay() with runBlockingTest and advanceTimeBy() but the thing is loadingJob.cancel() will be called before I ...
Read more >kotlinx-coroutines-test
This is helpful when one wants to execute a test in situations where the platform Main dispatcher is not available, or to replace...
Read more >Testing Kotlin Coroutines - Kt. Academy
We just use runBlocking , and there is nearly no difference between testing how suspending and blocking functions behave. Testing time dependencies. The ......
Read more >Testing Coroutines — Update 1.6.0 | by Ralf Stuckert - Medium
The building blocks of the kotlinx-coroutines-test module have changed a bit. ... You may now use multiple TestDispatcher s in a test, but...
Read more >Unit Testing Kotlin Channels & Flows - Android Summit
In this talk, I will share with you how to implement and test ... how to use features in the coroutines library such...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
@ebenfield’s example is also broken - it both makes the test take the real-world time of the delay inside the flow { }, and also the test will pass even when removing the advanceTimeBy call
i.e. this test takes 10 seconds, and also passes
This is because turbine ignores the current dispatcher when launching the flow, so delay() actually delays, and the test { } block does run in the test dispatcher, which means expectItem’s timeouts don’t work
What does work is to use flowOn
Now the body of the flow is the thing running in the test dispatcher, so the delay controller works, and also so do the turbine timeouts.
IMO it would be nice if the
runBlockingTest
example was made to work somehow as it feels surprising that it doesn’t. Also thebrokenTest
example is dangerous as with short delays it’s not clear that anything’s wrong, and it’s silently just not testing anything. Perhaps turbine should internally force the expectEvent() timeout to be 0 when using the test dispatcher?With the help of @dkhalanskyjb I found a way to make it work with the new
runTest
:Does it make sense to incoroprate this behavior direclty into turbine?