https://kotlinlang.org logo
#squarelibraries
Title
# squarelibraries
t

Tim Malseed

11/26/2023, 11:48 PM
Hi team, I’ve got a testing question. It only very loosely relates to Turbine, I’m sure it’s a more general coroutines/testing problem. But I thought I’d ask here anyway. It’s to do with testing the interactions between a Channel and a SharedFlow. See 🧵
Suppose we have this class:
Copy code
class SystemUnderTest(
    externalEvents: SharedFlow<Int>
) {
    private val _internalEvents = Channel<String>()
    val internalEvents = _internalEvents.receiveAsFlow()

    val mappedEvents = externalEvents.map { number ->
        _internalEvents.send(number.toString())
    }
}
This is a representation of a real ViewModel that I’m dealing with. Ideally we’d refactor it, but let’s say we need test coverage first.. So, here’s a test that tries to assert that when an external event receives a new value, our internal channel is updated:
Copy code
class MixedFlowsTest() {

    @Test
    fun testCase() = runTest {
        // Arrange
        val externalEvents = MutableSharedFlow<Int>()
        val systemUnderTest = SystemUnderTest(externalEvents)

        // Act
        systemUnderTest.internalEvents.test {
            externalEvents.emit(1)

            // Assert
            Assertions.assertEquals("1", awaitItem())
        }
    }
}
This doesn’t work.. presumably it’s because nothing is collecting
mappedEvents
, so
_internal_events.send()
never gets called
We can try to fix that like so:
Copy code
class MixedFlowsTest() {

    @Test
    fun testCase() = runTest {
        // Arrange
        val externalEvents = MutableSharedFlow<Int>()
        val systemUnderTest = SystemUnderTest(externalEvents)

        val job = launch {
            systemUnderTest.mappedEvents.collect()
        }

        // Act
        systemUnderTest.internalEvents.test {
            externalEvents.emit(1)

            // Assert
            Assertions.assertEquals("1", awaitItem())
        }

        // Cleanup

        job.cancel()
    }
}
But still, the test fails with ‘no value produced’
I’m guessing it’s a timing issue around when the events are emitted vs. when they are collected. But I really don’t understand what’s going on
Moving
externalEvents.emit(1)
to its own
launch{}
block, before the
.test{}
seems to solve the problem. I think it’s to do with the code indefinitely suspending on that
emit
call. I don’t understand it tho