Skip to content

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

@ZakTaccardi

Description

@ZakTaccardi

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?

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions