New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How can you use runBlockingTest with a coroutine you do not cancel? #1531
Comments
You need to explicitly cancel the actor before the end of the test. |
@elizarov but the actor coroutine is currently suspended.
The I also don’t see what problem throwing the exception solves - why does the actor coroutine need to be cancelled manually? Why can’t |
|
I guess I'm still confused. I do the following to work around the issue - is it invalid? fun runBlockingTestAllowUncompletedCoroutines(testBody: suspend TestCoroutineScope.() -> Unit) {
var testsPassed = false
try {
runBlockingTest {
testBody(this)
testsPassed = true
this.cancel()
}
} catch (exception: UncompletedCoroutinesError) {
if (testsPassed) {
// we are okay - this is a workaround for https://github.com/Kotlin/kotlinx.coroutines/issues/1531
} else {
throw exception
}
}
} I don't understand why I need to explicitly cancel the actor. Specifically: val scope: CoroutineScope = ..
val actor = scope.actor {
// ..
} The point of structured concurrency here is that when I cancel the What I am testing might be a runBlockingTest {
val scope = this
val viewModel = ViewModel(scope)
} The above test will fail with |
This actually doesn't work as you can't cancel a |
@ZakTaccardi that may not work, I'm on my phone and I was searching / reflecting as I also struggled hard today with testing Channel's. I think that this might be working val scope = this + TestCoroutineDispatcher()
ViewModel (scope)
...
scope.cancel() @elizarov does that sound like a proper form? |
I'd also expect the scope to cancel when runBlockingTest lambda completes |
I'm also facing this issue and could not find any workaround , did anyone find a solution for this issue , Thank you ? |
I guess that is not the case , because even in my code , it does not cancel the job after work is done .. any solution you found ?? |
The idea behind Consider this contrived code: repeat(1000) {
val deferred = CompletableDeferred<Int>()
launch {
deferred.await()
}
} If we never cancel the scope in which this happens, the coroutines that have no chance of ever running will use up the space. Now, consider some code (that's quite possible in real life) that periodically creates a coroutine that never runs. Now, this would be a full-fledged memory leak. If we just canceled the scope before ending the test, we would have no chance of learning about these leaks.
No longer true, now the scopes properly have |
I'm not sure if I like this behaviour or not. What is the purpose of asserting that all coroutines are done at the end of I still think it is a bit annoying to have to run the cleanup code of our class in every test. Is there a way to opt-out of this behaviour of |
Closing in favor of #3287, which is more up-to-date (much information here is not applicable anymore) and contains a general overview of the problem. |
In my test code, I have an ongoing
actor
coroutine that I do not need to directly cancel - I rely on the coroutine scope to cancel it. The below is a simplified example of my production code, which does not directly expose the actor coroutine to test code, so I cannot directly cancel it (obviously, callingactor.close()
allows the test to pass).Looking at
cleanupTestCoroutines()
- it saysThe actor is suspended - but why does the exception still get thrown? Note - I also tried calling
cleanupTestCoroutines
inside therunBlockingTest
lambda, but still no luck. Am I doing something wrong?The text was updated successfully, but these errors were encountered: