https://kotlinlang.org logo
#coroutines
Title
# coroutines
a

azabost

03/11/2020, 10:58 AM
Hi. I’m not sure if it is a bug or I treat the docs too literally so please advise. The doc for
Flow.launchIn()
says it is “a shorthand for `scope.launch { flow.collect() }`” but when I compare behaviors in unit tests it is a little different. Having a simple flow:
Copy code
private val integersFlow = flow {
        var i = 1
        while(true) {
            delay(100)
            emit(i++)
        }
    }
and using test scope and dispatcher:
Copy code
private val observingScopeJob1 = SupervisorJob()
private val observingScope1 = CoroutineScope(observingScopeJob1)
private val testDispatcher = TestCoroutineDispatcher()
When I do the following:
Copy code
@Test
    fun `should receive 10 integers - collect`() = runBlocking {
        var observedIntegers = 0

        observingScope1.launch(testDispatcher) {
            integersFlow.collect {
                observedIntegers++
            }
        }

        observedIntegers.shouldEqual(0)
        testDispatcher.advanceTimeBy(1000)
        observedIntegers.shouldEqual(10)
        Unit
    }
the test passes but when I convert it into RxJava-style like this:
Copy code
@Test
    fun `should receive 10 integers - launchIn`() = runBlocking {
        var observedIntegers = 0

        integersFlow
            .onEach { observedIntegers++ }
            .flowOn(testDispatcher)
            .launchIn(observingScope1)

        observedIntegers.shouldEqual(0)
        testDispatcher.advanceTimeBy(1000)
        observedIntegers.shouldEqual(10)
        Unit
    }
the test fails unless I add an additional
delay(100)
before
testDispatcher.advanceTimeBy(1000)
which seems very weird to me. Like… why is that needed to delay the main thread in unit test to allow the collecting coroutine to be launched at all?
l

louiscad

03/11/2020, 11:39 AM
Hi @azabost Your two snippets are not equivalent because one uses
flowOn
while the other changes the dispatcher of the scope. Two solutions to have equivalence between the two: 1. Don't pass
testDispatcher
to
launch
and use
flowOn
instead, like in the second snippet. 2. Remove
flowOn
and put the
testDispatcher
in the scope (e.g. using
launchIn(observingScope1 + testDispatcher)
).
a

azabost

03/11/2020, 12:25 PM
I used the 2. approach and it works as expected. Thanks for the explanation 🙏