Navigation Tests

Testing Navigation and User Interaction

Gestures are actions too: performTouchInput { swipeLeft(startX = centerX, endX = centerX - 1_000f) } on The Quiet Harbor drags the card through the cart's hand-written swipe-to-remove (Drag Gestures), and the test asserts that onRemove received that book. The app's own log line came out in the test report: D/BookNestAnim: released at -909 of 288 px: remove.

For navigation, BookNestNav gained one parameter, nav: NavHostController = rememberNavController(), so a test can pass a TestNavHostController (androidx.navigation:navigation-testing) and inspect the back stack. The graph calls hiltViewModel(), so the test is a @HiltAndroidTest hosted in HiltTestActivity, an empty @AndroidEntryPoint activity in the debug source set, and Hilt injects Testing with Fake Dependencies's fake repository, which now serves testBooks:

src/test/.../ui/nav/BookNestNavTest.kt: tap, then check the typed route (excerpt)Kotlin
@HiltAndroidTest
@Config(application = HiltTestApplication::class)
@RunWith(AndroidJUnit4::class)
class BookNestNavTest {
  @get:Rule(order = 0) val hilt = HiltAndroidRule(this)
  @get:Rule(order = 1) val compose = createAndroidComposeRule<HiltTestActivity>()
  private lateinit var nav: TestNavHostController
  @Before fun setUp() {
    compose.setContent {
      nav = TestNavHostController(LocalContext.current).apply {
        navigatorProvider.addNavigator(ComposeNavigator())
      }
      BookNestNav(testBooks, nav)
    }
  }
  @Test fun tappingABookOpensItsDetail() {
    compose.onNodeWithText("Salt and Saffron").performClick()
    val entry = nav.currentBackStackEntry!!
    assertTrue(entry.destination.hasRoute<BookDetail>())
    assertEquals(3, entry.toRoute<BookDetail>().id)                 // typed arguments
    compose.onNodeWithText("Add to cart").assertIsDisplayed()
  }

A second test adds the book, taps the Cart tab and asserts "Cart: 1 books, $24.00". Hilt's rule runs first (order = 0) so its component exists before the activity starts.