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)
}
}