Roberto Leinardi
05/05/2022, 4:43 PM@RunWith(AndroidJUnit4::class)
class MainActivityTest {
@get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()
@Test
fun appStartsWithoutCrashing() {
composeTestRule.apply {
// Check Switch
onNodeWithTag(FirstScreen.CONSENT_SWITCH)
.assertIsDisplayed()
.assertIsOff()
.performClick()
.assertIsOn()
// Click accept button
onNodeWithTag(FirstScreen.ACCEPT_BUTTON)
.assertIsDisplayed()
.performClick()
// Thread.sleep(500)
// Check we are inside the second screen
onNodeWithTag(SecondScreen.USERNAME_TEXT_FIELD)
.assertIsDisplayed()
}
}
}
I'm sure that is a timing issue because if I add a Thread.sleep(500)
before the onNodeWithTag(SecondScreen.USERNAME_TEXT_FIELD).assertIsDisplayed()
, the test is successful. But I would like to avoid `Thread.sleep()`s in my code.
Is there a better way to tell the composeTestRule
to wait for the NavHost to load the new content before executing proceeding with the next assertion?
PS
I know that would be better to test the Composables in isolation, but I really need to simulate the user input on the App using Espresso and not only test the Composable behavior.Alex Vanyo
05/05/2022, 6:09 PMComposeTestRule.waitForIdle
might be your best bet to wait for everything to be idle before continuing. If you haven’t seen it already, https://developer.android.com/jetpack/compose/testing#sync goes through the different APIs and methods available for avoiding those pesky Thread.sleep
sRoberto Leinardi
05/05/2022, 6:19 PMwaitForIdle()
and it did not workAlex Vanyo
05/05/2022, 6:24 PMComposeTestRule.waitUntil
would be your next best bet thenRoberto Leinardi
05/05/2022, 6:57 PMwaitUntil
?Alex Vanyo
05/05/2022, 7:17 PMcomposeTestRule.waitUntil {
composeTestRule
.onAllNodesWithTag(SecondScreen.USERNAME_TEXT_FIELD)
.fetchSemanticsNodes().size == 1
}
I’ll plug Jose’s recent article too which goes into more detail: https://medium.com/androiddevelopers/alternatives-to-idling-resources-in-compose-tests-8ae71f9fc473Ian Lake
05/06/2022, 3:22 AMNavController
in your test? Navigation only moves the current screen to RESUMED
when the animation completes, which is exactly the kind of condition you'd want to use with waitUntil
Crossfade
for Navigation itself, AnimatedContent
if you are using Accompanist Navigation Animation), so waitForIdle
should certainly work to wait for those animations to complete. I wonder if there's some one frame delay (i.e., would two back to back calls to waitForIdle()
actually make it work)Roberto Leinardi
05/06/2022, 8:34 AMNavController
but I suspect that there will still be some delay between the NavController
changing the current route and the composables being actually available. But it's just a guess.
@Alex Vanyo hey that actually worked! Thanks! I'm now checking Jose's medium post 🙂