5

I'm trying to test a Flow that uses shareIn with Turbine, but I'm a bit lost why my tests are failing and how I can fix it.

class MyTest {

    private val scope = CoroutineScope(Dispatchers.Default)
    private val mutableSharedFlow = MutableSharedFlow<Int>()

    @Test
    fun succeeds() = runBlocking {
        val sharedFlow = mutableSharedFlow

        sharedFlow.test {
            expectNoEvents()
            mutableSharedFlow.emit(3)
            expect(expectItem()).toBe(3)
        }
    }

    @Test
    fun fails() = runBlocking {
        val sharedFlow = mutableSharedFlow
            .shareIn(scope, started = SharingStarted.WhileSubscribed())

        sharedFlow.test {
            expectNoEvents()
            mutableSharedFlow.emit(3)
            expect(expectItem()).toBe(3)
        }
    }
}

In these tests, the first succeeds() test runs fine, but as soon as I include shareIn in the fails() test, the test fails with a timeout:

Timed out waiting for 1000 ms
kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1000 ms
    (Coroutine boundary)
    at app.cash.turbine.ChannelBasedFlowTurbine$expectEvent$2.invokeSuspend(FlowTurbine.kt:238)
    at app.cash.turbine.ChannelBasedFlowTurbine$withTimeout$2.invokeSuspend(FlowTurbine.kt:206)
    at app.cash.turbine.ChannelBasedFlowTurbine.expectItem(FlowTurbine.kt:243)

What should I do to test flows that use shareIn?

nhaarman
  • 98,571
  • 55
  • 246
  • 278

1 Answers1

8

I do not know why you've decided to use a scope with Dispatchers.Default as here:

...
private val scope = CoroutineScope(Dispatchers.Default)
...

For tests, just use Dispatchers.Unconfined instead because it executes coroutines immediately on the current thread and that's exactly what you need there.

...
private val scope = CoroutineScope(Dispatchers.Unconfined)
...

So, after applying the change above, both of your tests passed successfully.

You can find my sample project for this question here.

Anatolii
  • 14,139
  • 4
  • 35
  • 65