You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Robolectric测试中为NavHost可组合项传入必填导航参数

问题

我想在Robolectric测试中传入必填导航参数,以运行底层可组合项。我的代码基于TiVi应用旧版本的导航实现,但该项目没有相关测试。

实现代码如下:

fun NavGraphBuilder.addTopLevelComposable(
    navController: NavHostController,
    ... etc
) {
   addLowLevelComposable(
        navController = navController,
    ... etc
  )
}

private fun NavGraphBuilder.addLowLevelComposable(
    navController: NavHostController,
    ... etc
) {
    composable(
        LeafScreen.LowLevelDetails.route,
        arguments = listOf(
            navArgument("id") {
                type = NavType.StringType
                nullable = false
            },
        ),
    ) { backStackEntry ->
        val arguments = checkNotNull(backStackEntry.arguments)
        val id = checkNotNull(arguments.getString("id"))
        // 使用id的代码逻辑
    }
}

测试代码如下:

@Test
fun `addTopLevelComposable- low level destination - asserts something`() {
    composeTestRule.setContent {
        NavHost(
            navController = rememberNavController(),
            // 这个参数没有被正确处理!为什么?
            startDestination = LeafScreen.LowLevelDetails.createRoute("this is an id"),
        ) {
            addTopLevelComposable(
                navController = navHostController,
                // 其他参数
            )
        }
    }

    // 断言逻辑
}

data object LowLevelDetails: LeafScreen("home/lowlevel/{id}") {
    fun createRoute(id: String): String =
        "home/lowlevel/$id"
}

目前测试中设置的startDestination参数未被正确处理,请问该如何实现必填导航参数的正确传递?


解决方法

问题核心是:直接把带参数的字符串路由设为startDestination时,导航组件只会把整个字符串当作路由名,不会自动解析其中的参数占位符并填充到backStackEntry.arguments里。可以通过以下两种方式解决:

方式一:手动创建带参数的起始BackStackEntry

初始化NavHostController时,手动构建包含参数的起始条目,替代直接设置startDestination:

@Test
fun `addTopLevelComposable- low level destination - asserts something`() {
    composeTestRule.setContent {
        val navController = rememberNavController()
        
        LaunchedEffect(Unit) {
            // 构建带参数的Bundle
            val args = bundleOf("id" to "this is an id")
            // 初始化导航图并传入参数
            navController.setGraph(
                navController.navInflater.inflate(R.navigation.your_nav_graph),
                args
            )
        }

        NavHost(
            navController = navController,
            // 无需再设置startDestination
        ) {
            addTopLevelComposable(
                navController = navController,
                // 其他参数
            )
        }
    }

    // 断言逻辑
}

如果没有XML导航图,可以直接代码构建导航图:

LaunchedEffect(Unit) {
    val navGraph = navController.createGraph(
        startDestination = LeafScreen.LowLevelDetails.route,
        route = "root"
    ) {
        addTopLevelComposable(
            navController = navController,
            // 其他参数
        )
    }
    navController.setGraph(navGraph, bundleOf("id" to "this is an id"))
}

方式二:用navigate方法跳转目标路由

初始化NavHost后,通过navigate方法跳转到带参数的路由,确保参数被正确解析:

@Test
fun `addTopLevelComposable- low level destination - asserts something`() {
    composeTestRule.setContent {
        val navController = rememberNavController()
        NavHost(
            navController = navController,
            // 设置临时起始页(或直接用目标路由)
            startDestination = "temp_start"
        ) {
            // 临时起始页(仅用于初始化)
            composable("temp_start") {}
            addTopLevelComposable(
                navController = navController,
                // 其他参数
            )
        }

        // 手动跳转到带参数的目标页面
        LaunchedEffect(Unit) {
            navController.navigate(LeafScreen.LowLevelDetails.createRoute("this is an id"))
        }
    }

    // 断言逻辑
}

内容的提问来源于stack exchange,提问作者Alix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 10:16:19