question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How can you use runBlockingTest with a coroutine you do not cancel?

See original GitHub issue

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, calling actor.close() allows the test to pass).

@Test
fun runBlocking_() = runBlocking<Unit> {
    val emittedNumbers = mutableListOf<Int>()
    val actor = actor<Int> {
        for (i in channel) {
            emittedNumbers.add(i)
        }
    }
    actor.send(1)
    actor.send(2)
    // fails here with:
    // org.junit.ComparisonFailure: 
    // Expected :[1, 2]
    // Actual   :[1]
    assertThat(emittedNumbers)
        .isEqualTo(listOf(1, 2))
}
@Test
fun runBlockingTest_() = runBlockingTest {
    val emittedNumbers = mutableListOf<Int>()
    val actor = actor<Int> {
        for (i in channel) {
            emittedNumbers.add(i)
        }
    }
    actor.send(1)
    actor.send(2)
    assertThat(emittedNumbers)
        .isEqualTo(listOf(1, 2))
    
    // fails here with:
    // kotlinx.coroutines.test.UncompletedCoroutinesError: Test finished with active jobs:
}

Looking at cleanupTestCoroutines() - it says

@throws UncompletedCoroutinesError if any pending tasks are active, however it will not throw for suspended coroutines.

The actor is suspended - but why does the exception still get thrown? Note - I also tried calling cleanupTestCoroutines inside the runBlockingTest lambda, but still no luck. Am I doing something wrong?

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:12 (4 by maintainers)

github_iconTop GitHub Comments

10reactions
ZakTaccardicommented, May 8, 2020

scope.cancel() would also work

This actually doesn’t work as you can’t cancel a TestCoroutineScope, it throws an exception

8reactions
ursusursuscommented, Mar 31, 2021

I’d also expect the scope to cancel when runBlockingTest lambda completes

Read more comments on GitHub >

github_iconTop Results From Across the Web

Unit testing coroutines runBlockingTest: This job has not ...
Don't use flow.collect directly inside runBlockingTest . It should be wrapped in launch; Don't forget to cancel TestCoroutineScope in the ...
Read more >
runBlockingTest - Kotlin
This method requires that all coroutines launched inside testBody complete, or are cancelled, as part of the test conditions. Unhandled exceptions thrown by ......
Read more >
Unit Testing with Kotlin Coroutines: The Android Way - Medium
What will we learn? Testing suspend functions with runBlocking() and runBlockingTest(); Test Coroutines running on Dispatchers.
Read more >
Unit Testing Coroutine Suspend Functions using ... - craigrussell
This feels like it should work, and I don't know why it doesn't. Maybe it's a bug in the current implementation of runBlockingTest...
Read more >
Kotlin coroutine unit testing the better way - LogRocket Blog
If you are programming in Kotlin, you most likely use coroutines for ... inside the coroutine are not propagated as test failures, we'll ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found