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.

Test passes for 'runBlocking' but fails for 'runBlockingTest'

See original GitHub issue

The test below passes when runBlocking is used but fails with runBlockingTest.

sealed class Event {
  data class A(val id: Long) : Event()
  data class B(val id: Long) : Event()
}

class Test {
  @Test fun blocking() = runBlocking {
    val events = flowOf(Event.A(0), Event.B(0)).broadcastIn(this).asFlow()

    val a = events.filterIsInstance<Event.A>().map { "$it" }
    val b = events.filterIsInstance<Event.B>().map { "$it" }

    val strings = flowOf(a, b).flattenMerge().toList()

    Assert.assertEquals(strings, listOf(Event.A(0), Event.B(0)).map { "$it" })
  }

  @Test fun blockingTest() = runBlockingTest {
    val events = flowOf(Event.A(0), Event.B(0)).broadcastIn(this).asFlow()

    val a = events.filterIsInstance<Event.A>().map { "$it" }
    val b = events.filterIsInstance<Event.B>().map { "$it" }

    val strings = flowOf(a, b).flattenMerge().toList()

    Assert.assertEquals(strings, listOf(Event.A(0), Event.B(0)).map { "$it" })
  }
}

Events are missing from the expected result.

java.lang.AssertionError: 
Expected :[A(id=0)]
Actual   :[A(id=0), B(id=0)]

Kotlin: 1.3.50. Coroutines: 1.3.2.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:10
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
fblampecommented, Jul 28, 2021

Are there any news or a timeline for this? Removing delays in tests via RunBlockingTest would be very helpful, but only if it works reliably without a lot of extra coding.

0reactions
RBusarowcommented, Dec 2, 2019

Hmmm, but how does that work for runBlocking?

If I need to have the same dispatcher, then should I encapsulate all Dispatchers to keep them under my control? It looks like I’m gonna need to add a lot of dependencies for every case I need to use coroutines, my fear is to couple too much our code into the way coroutines works.

Yes, that’s a common complaint. The typical solution is to wrap all references up into a single injectable object like so:

interface DispatcherProvider {
  val main: CoroutineDispatcher
  val mainImmediate: CoroutineDispatcher
  ...
}

A different solution is in the works for #1365 , where you’ll be able to use Dispatchers.setDefault(...) to manipulate that singleton in lieu of all the injection, but now you’re manipulating a shared state in your testing.

You can do things to mitigate this issue, such as only specifying the dispatcher when it’s really necessary (for instance immediately before doing I/O in a repository). Typically, you shouldn’t need to specify the dispatcher as the main thread is typically capable of much more than we give it credit for. Also, if you’re using a third party library with coroutine support for I/O, such as Room or Retrofit, they already use their own dispatchers internally, so switching before calling a suspend function in their library is just wasteful.

Not to plug, but a final option would be my not-quite-complete little library DispatcherProvider, which uses the CoroutineContext itself to hold the interface described above. This way, the dispatchers are set upon creation of a CoroutineScope and are accessible from any coroutine or suspend function via the coroutineContext property.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Unit testing coroutines runBlockingTest: This job has not ...
I tried to replace runBlockingTest with runBlocking but the test seems to wait in an infinite loop. Can someone help me with this...
Read more >
Unit Testing with Kotlin Coroutines: The Android Way - Medium
We can mitigate the problem by using the runBlocking() function. This function runs a new Coroutine and blocks the current thread interruptibly ...
Read more >
runBlockingTest - Kotlin
runBlockingTest ... Executes a testBody inside an immediate execution dispatcher. This method is deprecated in favor of runTest. Please see the migration guide ......
Read more >
Testing Kotlin Coroutines - Kt. Academy
How we test Kotlin Coroutines, including common Android and backend cases. ... we practically need nothing else but runBlocking and classic tools for ......
Read more >
Say goodbye to Flakiness: Testing Coroutines and Kotlin ...
While runBlocking will wait for the amount of the delay , runBlockingTest will skip past any ... Run the test again and you...
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