Skip to content

Espresso interactions deadlock somewhere after the first successful test #78

@mannodermaus

Description

@mannodermaus

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 keep ActivityScenario 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:

// ...
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith

@ExtendWith(RobolectricExtension::class)
class ActivityScenarioTest {
    @Test
    fun `test 1`() {
        println("1")
        val scenario = ActivityScenario.launch(MainActivity::class.java)

        println(" - check")
        onView(withText("Changed")).check(doesNotExist())
        println(" - click")
        onView(withText("Click me")).perform(click())
        println(" - verify")
        onView(withText("Changed")).check(matches(isDisplayed()))

        scenario.close()
    }

    @Test
    fun `test 2`() {
        println("2")
        val scenario = ActivityScenario.launch(MainActivity::class.java)

        println(" - check")
        onView(withText("Changed")).check(doesNotExist())
        println(" - click")
        onView(withText("Click me")).perform(click())
        println(" - verify")
        onView(withText("Changed")).check(matches(isDisplayed()))

        scenario.close()
    }
}
Screenshot 2024-07-22 at 15 47 24

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:

// ...
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class ActivityScenarioTest {
    @Test
    fun `test 1`() {
        println("1")
        val scenario = ActivityScenario.launch(MainActivity::class.java)

        println(" - check")
        onView(withText("Changed")).check(doesNotExist())
        println(" - click")
        onView(withText("Click me")).perform(click())
        println(" - verify")
        onView(withText("Changed")).check(matches(isDisplayed()))

        scenario.close()
    }

    @Test
    fun `test 2`() {
        println("2")
        val scenario = ActivityScenario.launch(MainActivity::class.java)

        println(" - check")
        onView(withText("Changed")).check(doesNotExist())
        println(" - click")
        onView(withText("Click me")).perform(click())
        println(" - verify")
        onView(withText("Changed")).check(matches(isDisplayed()))

        scenario.close()
    }
}
Screenshot 2024-07-22 at 15 50 00

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions