Open mannodermaus opened 1 month ago
It took a bit of time, but I was finally able to trace this down to the fact that RobolectricExtension
will reset the main looper after each test. This doesn't jive with Espresso's usage of Dagger to inject the original main looper as a singleton into all of its view assertions (link; search for provideMainLooper
and provideMainThreadExecutor
). After the first Robo+JUnit5 test destroys the main looper, the next test receives a different one and the chain is broken. Espresso will wait indefinitely on a result using a LinkedBlockingQueue
in its InteractionResultsHandler
(link), causing the blockage.
I'd like to inquire first about the reason to reset sMainLooper
and sThreadLocal
after a test. If I comment this out, my tests run, but obviously there was a reason you added this in the first place. Maybe we can find an alternative that keeps one main looper alive, but still clears stuff inside it (maybe through delegation)? Thanks in advance! Looking forward to the discussion.
@mannodermaus The primary reason for resetting the loopers at the end of each test is to ensure that the looper mode instrumentation functions correctly. If the loopers are not reset, tests may run on the main thread rather than the intended instrumented thread. This behavior is managed by Robolectric's resetLoopers
method in ShadowPausedLooper class which calls createMainThreadAndLooperIfNotAlive
to set up the necessary threads for instrumentation testing. I am not sure why this behavior occurs with JUnit 5, considering it works fine with JUnit 4. I would welcome any further insights you might have.
I have yet to dive deep into what causes this, but here is a side effect I have observed with Espresso assertions running in a JUnit 5 context that uses
RobolectricExtension
. It's probably the magic of switching thread contexts and class loaders that trips it up, but I wanted to hear your thoughts on the matter first. If I remove the Espresso stuff and only keepActivityScenario
around, then it seems to work no matter how many tests I run. Also, this happens with any kind of test (@ParameterizedTest
,@RepeatedTest
etc), but I am using the base@Test
to make it as simple as possible.With JUnit 5
The following test class will execute "test 1" successfully, then deadlock in "test 2" just before the first
onView().check()
assertion:With JUnit 4
When changing this to a JUnit 4 environment (i.e. change
@Test
annotations, then replace@ExtendWith
with@RunWith
), all tests are okay:I have pushed an example to a branch in my fork: https://github.com/mannodermaus/junit5-robolectric-extension/blob/check/androidx-robolectric-espresso-deadlock/integration-tests/agp-kotlin-dsl/src/test/kotlin/tech/apter/junit/jupiter/robolectric/integration/tests/agp/kotlin/dsl/ActivityScenarioTest.kt