How to construct unit tests combining Robolectric and AndroidX with Jetpack Compose?

887 views Asked by At

Assumptions

  • Robolectric controls which Android version to emulate, so I need this
  • AndroidX has the test API for Jetpack Compose, so I need this as well

Background

I have experienced a lot of AppNotIdleException when running the full test suite, but most often they run successfully in isolation (related: https://github.com/robolectric/robolectric/issues/7055). I would like to understand exactly how these two APIs should be combined to avoid problems.

For starters there is a configuration called "Looper mode" in Robolectric, which basically defines when different threads are executed. The default value, since Robolectric 4.5, is "PAUSED" which means the developer should control when threads are executed.

In addition AndroidX offers createComposeRule() which makes it possible to build minimal unit tests with a single composable - very nice. This "Compose rule" comes with a "mainClock" with the default behavior to "auto-advance".

Question

Given a simple composable with a button. What is the recommended setup (annotations, clock configs, etc) for my test? Let's assume I want the test to run for Android P (API=28). Any feedback is welcome. I want to keep the tests as tidy as possible.

Code

This is how I would write my tests today:

@Config(sdk = [Build.VERSION_CODES.P])
@RunWith(AndroidJUnit4::class)
@LooperMode(LooperMode.Mode.PAUSED)
class MyComposablesKtTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    private val buttonNode get() = composeTestRule.onNodeWithContentDescription("My Button")

    @Before
    fun setUp() {
        composeTestRule.mainClock.autoAdvance = false
    }

    @Test
    fun `MyComposable - always - sets up view correctly`() {
        composeTestRule.setContent {
            MyComposable(onClick = {})
        }
        composeTestRule.mainClock.advanceTimeByFrame()

        buttonNode.assertExists().assertHasClickAction().assertIsEnabled()
    }

    @Test
    fun `MyComposable - when button clicked - performs action`() {
        var buttonClicked = false
        composeTestRule.setContent {
            MyComposable(onClick = { buttonClicked = true })
        }
        composeTestRule.mainClock.advanceTimeByFrame()

        buttonNode.performClick()

        assertTrue(buttonClicked)
    }

}

0

There are 0 answers