Olivier Patry
11/10/2021, 5:01 PMFragmentScenario
but I think it's something that could be discussed without considering such specific setup (let me know if it's not the case).
In a JUnit Android instrumentation test, I launch an Android fragment using FragmentScenario
, and my fragment impl uses view.doOnLayout{}
(would be the same with <http://view.post|view.post>{}
).
To make my test pass, I need the code being executed in such postponed code.
Here comes the link to coroutines:
My test is launched on the main thread, once fragment.onCreate
is called, I'd need to let "app logic" (here Android, but I guess it could be applicable to another use case) work and in particular get access to the main thread.
My idea was to suspend the execution of the test to give back the access to main thread to whoever needs it but I can't make it work.
@Test
fun testMyState() {
launchMyFragmentFragment(Lifecycle.State.RESUMED) {
mainCoroutineRule.runBlocking {
delay(100)
assertEquals(3, myState.count())
}
}
}
I would have expected that triggering a suspension using delay
would give back access to main thread to my "app logic" (here Fragment.view.doOnLayout
) but it doesn't work.
Is it, by chance, a use case some know how to address?Joffrey
11/10/2021, 7:43 PMrunBlocking
blocks the thread that calls it in order to execute what's inside. So basically the only thing that the blocked thread can run is the lambda inside runBlocking
. Even with delay()
the thread cannot go execute other coroutines.Olivier Patry
11/10/2021, 9:18 PMMainCoroutineRule.runBlocking
uses runBlockingTest
under the hood.
Is it the same issue?
Do you think there is ways to suspend JUnit main thread execution without ending the test?Nick Allen
11/11/2021, 2:30 AMrunCurrent()
to force any “ready” coroutine to run. It’s a method on `TestCoroutineScope`/`TestCoroutineDispatcher`.Olivier Patry
11/11/2021, 9:42 AMHandler
(I think), will it help in that situation too?
I'll try to check runCurrent()
I wasn't aware of, thanks.Nick Allen
11/12/2021, 1:04 AMMainCoroutineRule
that these were local tests, but I see in your description that they are instrumented (sorry, missed that before).
My guess is that MainCoroutineRule
sets Dispatchers.Main
to use a TestCoroutineDispatcher
and which is meant for local unit tests, not instrumented tests and that none of your test code is running on the main thread at all. TestCoroutineDispatcher
uses an internal fake clock so you can test code that delays without really delaying allowing your tests to run quickly.
I'd recommend just using the real main thread. I'm not sure how coroutines factors into this. If you just want to sync up with the main thread, you could try Instrumentation.runOnMainSync
. runBlocking(Dispatchers.Main)
would work too if end up wanting to do some coroutine stuff on the main thread.Olivier Patry
11/12/2021, 9:09 AMOlivier Patry
11/12/2021, 9:52 AMresetMain
+ used Instrumentation.waitForIdle
and it solved my issue.
Thanks for this really accurate input 👍