测试 Compose 导航

将导航代码与可组合项目的地分离开,以便独立于 NavHost 可组合项单独测试每个可组合项。

请勿直接将 NavController 传递给任何可组合项。而是将导航回调(lambda)作为参数传递。这样一来,您的所有可组合项均可单独测试,因为它们不需要在测试中使用 NavController 实例。

NavHost 中的 composable lambda 充当 Navigation API 与您的可组合项之间的桥梁:

@Composable
fun ProfileScreen(
    userId: String,
    navigateToFriendProfile: (friendUserId: String) -> Unit
) {
 // …
}

// In your NavHost
composable<Profile> { backStackEntry ->
    val profile = backStackEntry.toRoute<Profile>()
    ProfileScreen(userId = profile.id) { friendUserId ->
        navController.navigate(route = Profile(id = friendUserId))
    }
}

这样,通过传递模拟值和回调,即可独立于 Navigation 测试 ProfileScreen

建议通过测试 NavHost、传递给可组合项的导航操作以及各个屏幕可组合项来编写涵盖应用导航要求的测试。

测试 NavHost

如需开始测试 NavHost,请将以下导航测试依赖项添加到应用模块的 build.gradle 文件中:

dependencies {
  androidTestImplementation "androidx.navigation:navigation-testing:$navigationVersion"
}

将应用的 NavHost 封装在接受 NavHostController 作为参数的可组合项中:

@Composable
fun AppNavHost(navController: NavHostController){
  NavHost(navController = navController, startDestination = Home){
      composable<Home> { /*...*/ }
      composable<Profile> { /*...*/ }
  }
}

现在,您可以通过传递导航测试制品 TestNavHostController 的实例来测试 AppNavHostNavHost 内定义的导航逻辑。

用于验证应用启动目的地和 NavHost 的界面测试将如下所示:

class NavigationTest {

    @get:Rule
    val composeTestRule = createComposeRule()
    lateinit var navController: TestNavHostController

    @Before
    fun setupAppNavHost() {
        composeTestRule.setContent {
            navController = TestNavHostController(LocalContext.current)
            navController.navigatorProvider.addNavigator(ComposeNavigator())
            AppNavHost(navController = navController)
        }
    }

    @Test
    fun appNavHost_verifyStartDestination() {
        composeTestRule
            .onNodeWithContentDescription("Start Screen")
            .assertIsDisplayed()
    }
}

测试导航操作

您可以通过多种方式测试导航实现,方法是对界面元素执行点击,然后验证所显示的目的地,或将预期路线与当前路线进行比较。

如果您要测试具体应用的实现,最好在界面中执行点击操作。如需了解如何单独测试各个可组合函数,请参阅在 Jetpack Compose 中进行测试 Codelab。

您还可以使用 navController 检查导航断言,只需使用 currentBackStackEntry 将当前路线与预期路线进行比较即可:

@Test
fun appNavHost_clickProfile_navigatesToProfile() {
    composeTestRule.onNodeWithContentDescription("Go to Profile")
        .performClick()

    assertTrue(navController.currentBackStackEntry?.destination?.hasRoute<Profile>() ?: false)
}

如需详细了解 Compose 测试基础知识,请参阅测试 Compose 布局指南。