Trying to figure out how to test channels + flow b...
# coroutines
v
Trying to figure out how to test channels + flow but I keep running into a
IllegalStateException: This job has not completed yet
. I have a channel in my viewmodel which I expose as a flow ViewModel
Copy code
private val _channel = BroadcastChannel<Boolean>(1)
val myFlow = _channel.asFlow()

fun sendToChannel() {
    viewModelScope.launch {
        _channel.send(true)
    }
}
And in my activity, I collect the flow
Copy code
lifecycleScope.launch {
     myFlow.collect { println(it) }
}
My Test
Copy code
@Test
fun `myTest`() = runBlockingTest {
    
    viewModel.myFlow.collect { myVal ->
        assertTrue(myVal)
    }

    viewModel.sendToChannel()
}
How do I ensure the assertion is called. Or what’s a better way of testing this?
o
v
Weird, so there’s currently no way of testing a BroadcastChannel with Flow?
s
The
runBlockingTest
provides a TestCoroutineScope that dispatches on a TestCoroutineDispatcher. Your
viewModelScope
uses the Dispatchers.Main. This mismatch may be the problem, where sendToChannel really never winds up calling
_channel.send()
.
Use Dispatchers.setMain to make the Main dispatcher use the TestCoroutineScope's TestCoroutineDispatcher instead for your unit tests
v
I’m currently already doing that with the gist I got from the Google IO app
Copy code
@get:Rule
var coroutineRule = MainCoroutineRule()

@Test
fun myTest() = coroutineRule.runBlockingTest {}
https://gist.githubusercontent.com/manuelvicnt/049ce057fa6b5e5c785ec9fff7c22a7c/raw/b668143cbc1a8d52ef7267796b8a787165497702/CoroutinesTestRule.kt
👍 1
s
I looked at your myTest code.
collect
suspends and you never get to call viewModel.sendToChannel().
Wrap the
viewModel.myFlow.collect {...}
inside a
launch
....
v
If we wrap the collect call inside of
launch
it throws an
UncompletedCoroutinesError: Test finished with active jobs
. I assume this is because the flow is still open to collecting. Do I then need to call cancel inside the of launch? because that seems seems a little flaky to me but the test passes. Here’s what I have so far
Copy code
@Test
fun `myTest`() = coroutineRule.runBlockingTest {

    launch {
        viewModel.myFlow.collect { myVal ->
            assertTrue(myVal)
            cancel()
        }
    }
    viewModel.sendToChannel()
}
Was able to resolve the issue by using
.take(n).toList()
rather than
collect
. I think this might currently be the best way to test flow according to Manuel https://stackoverflow.com/a/58611767/5067435
t
I wrote that (long) post a few days ago to introduce a
Flow.test
operator. This might help you : https://kotlinlang.slack.com/archives/C1CFAFJSK/p1575206282080900