dimsuz
01/28/2022, 1:04 PMclass Feature {
private val scope = CoroutineScope()
private val _flow = MutableSharedFlow<Int>()
fun start() {
scope.launch { flow.emit(1); flow.emit(2) }
}
val flow: Flow<Int> = _flow
}
test code:
should("do something") {
val awaitFirstEmit = feature.flow.onStart { feature.start() }.first()
awaitFirstEmit shouldBe 1
}
Is it OK to use onStart
like this, or will this produce flakiness?
the important thing here is I want to ensure that first item is not lost.Joffrey
01/28/2022, 1:08 PMFeature.flow
was exposed as SharedFlow
, you could use onSubscription
for this, which is meant for that. As for Flow.onStart
, I'm not 100% sure there is this guaranteedimsuz
01/28/2022, 1:08 PMdimsuz
01/28/2022, 1:10 PMonSubscription
internallyJoffrey
01/28/2022, 1:16 PMonStart
is run before the upstream collection begins - effectively calling feature.start()
before subscribing to the shared flow.
You can prove it doesn't work by adding an atrifical delay after feature.start()
in onStart
in your test. It will hang forever if it missed the emissionsJoffrey
01/28/2022, 1:19 PMfirst()
you could consider it a guarantee that first
is reached by the main coroutine of the test before the emissions are done by the launched coroutine. But it's probably not your case here, since CoroutineScope()
likely uses the Default
dispatcher. And in any case I would personally not like to rely on that 😄dimsuz
01/28/2022, 1:38 PMDefault
by default 🙂 Thank you for suggestions and advice! Will think about how to best handle this!Nick Allen
01/28/2022, 5:56 PMshould("do something") {
val awaitFirstEmit = async { feature.flow.first() }
advanceUntilIdle() //This should guarantee the subscription
feature.start()
//might need another advanceUntilIdle() here but I don't think so
awaitFirstEmit.await() shouldBe 1
}
dimsuz
01/29/2022, 3:31 PM