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

如何测试使用Ramcosta导航库的Jetpack Compose页面

解决Compose Destinations导航库的UI测试问题

1. 核心问题:未覆盖正确的navigate重载

你的页面代码中调用的是navigator.navigate(ShoppingScreenDestination()),这是接收Destination对象的重载方法,但你手动实现的DestinationsNavigator只覆盖了接收字符串路由的版本,导致测试时调用未实现的方法抛出异常。

2. 手动实现带导航记录的Mock Navigator

你可以手动实现DestinationsNavigator,并记录导航行为,方便后续验证:

@HiltAndroidTest
@UninstallModules(AppModule::class)
@ExperimentalPermissionsApi
class MainPageScreenTest {

    @get:Rule(order = 0)
    val hiltRule = HiltAndroidRule(this)

    @get:Rule(order = 1)
    val composeRule = createAndroidComposeRule<MainActivity>()

    private lateinit var mockNavigator: TestDestinationsNavigator

    class TestDestinationsNavigator : DestinationsNavigator {
        // 记录导航到的Destination
        val navigatedDestinations = mutableListOf<Destination>()
        // 记录其他导航动作的标志
        var backStackCleared = false
        var navigateUpCalled = false
        var popBackStackCalled = false

        // 覆盖页面用到的navigate(Destination)重载
        override fun navigate(destination: Destination, onlyIfResumed: Boolean) {
            navigatedDestinations.add(destination)
        }

        // 其他方法根据测试需求实现,不需要验证的可以返回默认值
        override fun clearBackStack(route: String): Boolean {
            backStackCleared = true
            return true
        }

        override fun navigate(
            route: String,
            onlyIfResumed: Boolean,
            builder: NavOptionsBuilder.() -> Unit
        ): Boolean {
            navigatedDestinations.add(object : Destination {
                override val route: String = route
            })
            return true
        }

        override fun navigateUp(): Boolean {
            navigateUpCalled = true
            return true
        }

        override fun popBackStack(): Boolean {
            popBackStackCalled = true
            return true
        }

        override fun popBackStack(
            route: String,
            inclusive: Boolean,
            saveState: Boolean
        ): Boolean {
            popBackStackCalled = true
            return true
        }
    }

    @Before
    fun setup() {
        hiltRule.inject()
        mockNavigator = TestDestinationsNavigator()
        composeRule.activity.setContent {
            GroceryCostTheme {
                MainPageScreen(navigator = mockNavigator)
            }
        }
    }

    // 测试购物篮按钮的导航行为
    @Test
    fun clickShoppingIcon_navigatesToShoppingScreen() {
        // 点击购物篮IconButton
        composeRule.onNodeWithContentDescription("Shop").performClick()

        // 验证是否导航到了ShoppingScreenDestination
        assert(mockNavigator.navigatedDestinations.first() is ShoppingScreenDestination)
    }
}

3. 用MockK简化Mock实现

如果使用MockK框架(需要在build.gradle添加依赖),可以更简洁地Mock导航器,避免手动实现所有方法:

首先添加模块级依赖:

androidTestImplementation("io.mockk:mockk-android:1.13.8")

然后修改测试代码:

@HiltAndroidTest
@UninstallModules(AppModule::class)
@ExperimentalPermissionsApi
class MainPageScreenTest {

    @get:Rule(order = 0)
    val hiltRule = HiltAndroidRule(this)

    @get:Rule(order = 1)
    val composeRule = createAndroidComposeRule<MainActivity>()

    private val mockNavigator: DestinationsNavigator = mockk(relaxed = true)

    @Before
    fun setup() {
        hiltRule.inject()
        composeRule.activity.setContent {
            GroceryCostTheme {
                MainPageScreen(navigator = mockNavigator)
            }
        }
    }

    @Test
    fun clickShoppingIcon_navigatesToShoppingScreen() {
        // 点击购物篮按钮
        composeRule.onNodeWithContentDescription("Shop").performClick()

        // 验证navigate方法是否被正确调用
        verify(exactly = 1) {
            mockNavigator.navigate(any<ShoppingScreenDestination>())
        }
    }
}

relaxed = true会让MockK自动为所有未指定的方法提供默认实现,无需手动覆盖每个方法,大幅简化代码。

4. 额外提示

  • Toast属于系统级控件,Compose测试工具无法直接捕获,建议将Toast逻辑封装到ViewModel或单独工具类中,方便Mock验证。
  • 确保ShoppingScreenDestination是可实例化的(如果是注解生成类,需确认生成代码已正确编译)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 12:44:53