kotlinx.coroutines: runBlockingTest fails with "This job has not completed yet"
This simple block:
runBlockingTest {
suspendCancellableCoroutine<Unit> { cont ->
thread {
Thread.sleep(1000)
cont.resume(Unit)
}
}
}
Fails with the exception:
Exception in thread "main" java.lang.IllegalStateException: This job has not completed yet
at kotlinx.coroutines.JobSupport.getCompletionExceptionOrNull(JobSupport.kt:1114)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:53)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest$default(TestBuilders.kt:45)
at SamplKt.main(Sampl.kt:8)
at SamplKt.main(Sampl.kt)
This looks like a bug to me as it should pass.
Kotlin 1.3.31 Coroutines 1.2.1
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 202
- Comments: 58 (11 by maintainers)
Commits related to this issue
- Request5ConcurrentKtTest irgnored until https://github.com/Kotlin/kotlinx.coroutines/issues/1204 is fixed / https://github.com/Kotlin/kotlinx.coroutines/pull/1206 merged and both released — committed to lotharschulz/intro-coroutines by lotharschulz 5 years ago
- Fix tests Had to migrate to runBlocking {} due to https://github.com/Kotlin/kotlinx.coroutines/issues/1204 — committed to chrisbanes/tivi by chrisbanes 4 years ago
- cleanup new tests / fix bug in RecorderRepositoryTest with using runBlocking instead of runBlockingTest / See more here: https://github.com/Kotlin/kotlinx.coroutines/issues/1204#issuecomment-549173908 — committed to Eloquent-Team/Eloquent-Android by deleted user 4 years ago
- Implement 'runTest' that waits for asynchronous completion (#2978) Implement a multiplatform runTest as an initial implementation of #1996. Fixes #1204 Fixes #1222 Fixes #1395 Fixes #1881 Fixe... — committed to Kotlin/kotlinx.coroutines by dkhalanskyjb 3 years ago
- Implement 'runTest' that waits for asynchronous completion (#2978) Implement a multiplatform runTest as an initial implementation of #1996. Fixes #1204 Fixes #1222 Fixes #1395 Fixes #1881 Fixes #191... — committed to Kotlin/kotlinx.coroutines by dkhalanskyjb 3 years ago
- Implement 'runTest' that waits for asynchronous completion (#2978) Implement a multiplatform runTest as an initial implementation of #1996. Fixes #1204 Fixes #1222 Fixes #1395 Fixes #1881 Fixes #191... — committed to Kotlin/kotlinx.coroutines by dkhalanskyjb 3 years ago
- Implement 'runTest' that waits for asynchronous completion (#2978) Implement a multiplatform runTest as an initial implementation of #1996. Fixes #1204 Fixes #1222 Fixes #1395 Fixes #1881 Fixes #191... — committed to Kotlin/kotlinx.coroutines by dkhalanskyjb 3 years ago
- Update kotlinx-coroutines-test (#2973) This commit introduces the new version of the test module. Please see README.md and MIGRATION.md for a thorough discussion of the changes. Fixes #1203 Fix... — committed to Kotlin/kotlinx.coroutines by dkhalanskyjb 3 years ago
- Update kotlinx-coroutines-test (#2973) This commit introduces the new version of the test module. Please see README.md and MIGRATION.md for a thorough discussion of the changes. Fixes #1203 Fix... — committed to yorickhenning/kotlinx.coroutines by dkhalanskyjb 3 years ago
- Update kotlinx-coroutines-test (#2973) This commit introduces the new version of the test module. Please see README.md and MIGRATION.md for a thorough discussion of the changes. Fixes #1203 Fix... — committed to pablobaxter/kotlinx.coroutines by dkhalanskyjb 3 years ago
Just use fun test() = runBlocking{} instead of TestCoroutineScope
On Sat, Jan 11, 2020, 6:40 PM Shane Witbeck notifications@github.com wrote:
Is there a workaround for this issue ?
@alan-camilo What do you think about making a cake and throwing a party to celebrate its anniversary? You guys coming? Drinks on me
Happens to my tests (not using livedata). Here are the tests: https://github.com/premnirmal/StockTicker/blob/master/app/src/test/kotlin/com/github/premnirmal/ticker/network/StocksApiTest.kt I have to use
runBlockingright now instead ofrunBlockingTestuntil this is fixedI came with this exception for some Room database tests (
androidTest) using theAndroidJUnit4runner. It stopped throwing the exception when I added theInstantTaskExecutorRulerule.I hope this helps!
any update on this?
@laim2003 JUnit, kotest and other testing frameworks allow you to generate tests dynamically.
When you combine this capability with a single entry-point with
runBlocking, you can run tests against asynchronous code in parallel so long as there’s no shared mutable state between the tests. This is also closer to real-world conditions, no hacks involved, so you test your code better than with hacks that don’t happen when your final program runs.So, IMO, saying testing asynchronous code is cancer is a bit of an overstatement. I think that with Kotlin, we actually have the best facilities in the industry for this, despite that rarely usable
runBlockingTestfunction.BTW, if you want to test flows, here’s a very good third-party library made from the renowned devs from Square: https://github.com/cashapp/turbine
@fluidsonic testing asynchronous code is cancer as long as they don’t fix it…
I have the same issue any update ?
I found it hard to find the official docs when searching for
runTest, so posting a direct link to help others find the right docs they’ll need when migrating away fromrunBlockingTest.Hi guys, I published a post on stackoverlow and just saw your response about this issue. I tried to replace the runBlockingTest by runBlocking but my test seems to wait in an infinite loop. Can someone help me pass this unit test please?
I’m using something like
Still having the same issue…
Patch https://github.com/Kotlin/kotlinx.coroutines/pull/1206 should fix it!
It turns out the behavior of completion in the presence of multiple threads had a non-deterministic outcome that could, in some cases, cause a random test result.
This patch fixes the issue reported here by making it pass (as expected) while providing a detailed error message and test failure if this race condition is detected.
Here is another minimal test case:
This fails with
This issue is fixed in
1.6.0-RCwhererunBlockingTestwas deprecated (for the sake of backwards compatibility) andrunTestwithout the described pitfall was introducedAny update on when this fix will be released?
If anyone interested in a temporary solution ➡️ If you have only one element expected,
Flow.first()can be used. Otherwise, I am using a helper method like below for multiple emissions until this issue is fixed. It has a very similar implementation withFlow.first()but returning a list of data and it only works if the number of expected elements are known.I had the same issue, turned be that I call the room query in my code in Dispatchers.IO Try it without defining any Dispatcher, Room run query in main-safe way!!
Having the same issue. But temporarily one can use runBlocking without having any issues
Another workaround is to use
runBlocking { … }selectively within yourrunBlockingTest { … }code. Likely won’t work if you launch new coroutines in the test dispatcher but works great when waiting for coroutines on other dispatchers.@objcode I’m not sure if this patch will fix this issue entirely.
E.g. this code, that uses no other threads, fails with the “This job has not completed” message"
while this code just finishes without any problems:
@LouisCAD
honestly my reply came from a short-term frustration so yes it was an overstatement. But thanks for the recommendations anyway
@fluidsonic, I follow this article and it works for me, you can try it.
If you need example, you can check this
I just ran into this issue.
Any update on a permanent fix for the issue or suggested workarounds would be appreciated.
Because it’s deprecated and often time making wrong tests pass.
@mnewlive you should migrate to runTest
Injecting dispatcher worked for me. The main point is to make sure you are using the same test dispatcher instance in the block inside
runBlockingTest { }.For my project, we have DI library to inject dispatchers, and when you override the dispatcher in tests, make sure you are using the same instance instead of creating new instances every time you ask for a dispatcher.
And then when you call
runBlockingTest, make sure you call from your test dispatcher instance, e.g.coroutineRule.testDispatcher.runBlockingTest { ... }instead of justrunBlockingTest {...}.I spoke more on this SO thread.
Similar to @fluidsonic, I need to test a suspend method which calls another suspend method that uses suspendCancellableCoroutine. Changing the dispatcher of the UT is not helping,
runBlockingis leaving me with an infinite loop,runBlockingTestandtestDispatcher.runBlockingTestthrow me the “This job has not completed yet” error. Hope it will be fixed, the thread is almost 2 years oldEDIT: in my case the fault was in a badly mocked class, using
runBlockingis enoughAnother minimal reproducible example:
This is on Kotlin 1.3.72 and Coroutines 1.3.8.
Workaround: like said in the previous comment, using
first()instead ofsingle()doesn’t crash.❗ I assume that the underlying mechanics of the crash are due to the same issue. If not, please create a separate issue for this, or let me know: then I’ll create a separate issue. This could be a problem with (
Mutable)StateFlowinstead of being related to this issue.To have your test run quicker, you can implement your own testing dispatcher, implementing Delay, where you would divide the requested time by the desired amount.
On Fri, Jan 24, 2020 at 12:20 PM Daniel Asher notifications@github.com wrote:
Same issue with this as of May 12. runBlockingTest throws error with job never completed. runBlocking results in the stuck being stuck in an infinite wait after the mocks are created.
EDIT: Found a way to bypass though by instead of Mocking my Mono/Flux requests. I gave it values instead so my awaitFirstOrNull method would actually return.
Thank you @NXAN2901. Unfortunately that doesn’t help in my case. When using
kotlinx-coroutines-reactiveit usessuspendCancellableCoroutineunder the hood which is not supported for testing as per this issue. Changing the coroutine context won’t work around this.Yes, but what is surprising here? Flow has contract, that can emit 0…Infinity number of items, it may complete or may never complete or may return an error, so all those behaviors are implementation details of a particular flow And you should design your test in a way which checks your expected behavior (so do you expect for Flow to complete or not
Hey everyone! I struggled with this exception too, but today found solution to avoid it. Add this dependency if you don’t have it
testImplementation "androidx.arch.core:core-testing:$versions.testCoreTesting"(2.1.0)Then in your test class declare
InstantTaskExecutorRulerule:Now
runBlockingTestwon’t throwIllegalStateException: This job has not completed yetP.S. Yep, I know that this rule usually used for LiveData testing. P.P.S. Would be cool, if someone will explain why it works 😄
@objcode If the code were regular non-suspendable code, just plain blocking code, writing this would hang the test forever as well:
The above test would never return.
You could make the case that if a non-suspending piece of code never completes, a corresponding suspending piece of code should never resume (and when wrapping that in a
runBlockingXXX { ... }block, the code would never return).Also, a 30 seconds timeout may not be enough for some tests… should the timeout be configurable?
In the end, it is a matter of taste 😄 Hanging tests are not good, but when regular non-suspending code never completes, the test will hang as well. Should that be the same for suspending code inside a
runBlockingTest { ... }block or should we use a timeout, keeping in mind that a plainrunBlocking { ... }will hang forever?