rook
11/24/2021, 3:20 PMcallbackFlow
. I want to ensure that the value emitted from the flow matches the value that the callback receives. My problem appears to be that the ProducerScope
block doesn’t get called until something subscribes to the flow. I thought that using shareIn
would fix my problem, but it still doesn’t appear to kick off the ProducerScope
in time
fun someTest() = runBlocking {
val expected = "someMessage"
val resultFlow = getCallbackFlow().shareIn(this, SharingStarted.Eagerly, replay = 1)
sendMessageToTriggerCallback(expected)
val actual = resultFlow.first()
assertEquals(expected, actual)
}
After adding some logging, I find that what happens is that first, sendMessageToTriggerCallback
is invoked, then the callback is registered in the ProducerScope
, then it suspends indefinitely awaiting the first signal from the resultFlow
. I’ve tried a lot of variations on this pattern, and I can’t seem to make the ProducerScope
run without blocking my ability to subsequently trigger sending the message. I’ve tried launching callback flow collection in its own job and sending the message a separate job and yielding between them. I’ve tried leveraging onSubscription
, but that doesn’t fix the issue either, I get the same execution order as the example above. I’m at a bit of a loss as to how to enforce a deterministic execution order in this test case.Joffrey
11/24/2021, 3:31 PMSharedFlow.onSubscription
should be what you need, though. It's strange that it doesn't work. Do you mind sharing your callbackFlow
definition?
Have you tried launching the first()
inside an async(start = UNDISPATCHED)
? This is what I usually do.rook
11/24/2021, 3:46 PMcallbackFlow
. It essentially binds a callback to an event bus. I have to scrub the impl, give me a minute.rook
11/24/2021, 3:51 PMJoffrey
11/24/2021, 3:55 PMrook
11/24/2021, 3:55 PMJoffrey
11/24/2021, 3:55 PMrook
11/24/2021, 3:56 PMrook
11/24/2021, 3:57 PMJoffrey
11/24/2021, 3:59 PMAdam Powell
11/24/2021, 4:17 PMCoroutineStart.ATOMIC
so it ends up in the dispatch queue even if you launch the collector coroutine undispatchedrook
11/24/2021, 4:37 PMrook
11/24/2021, 4:38 PMAdam Powell
11/24/2021, 4:47 PMcallbackFlow {}
builder that launches the producer block undispatched so that you don't miss events in cases like this: https://pl.kotl.in/2yeSbUSYQAdam Powell
11/24/2021, 4:48 PMawaitClose {}
block) and your flowOn
operator is introducing another dispatch stepAdam Powell
11/24/2021, 4:59 PMflowOn
at all; if it's not thread-safe and you're using the flowOn
to guarantee which thread you're registering/unregistering on, then you should add the flowOn
in your real/live integration code and omit it in the unit under test hererook
11/24/2021, 5:00 PMawaitClose
call that handles unregistering the callback.Adam Powell
11/24/2021, 5:01 PMrook
11/24/2021, 5:02 PM