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

如何在Hilt+Jetpack Compose的Instrumentation测试中填充SavedStateHandle导航参数

解决Hilt ViewModel测试中填充SavedStateHandle导航参数的简便方法

下面给你几个实用的解决方案,不用写大量样板代码就能搞定测试时的参数问题:

方案1:用Hilt测试模块替换SavedStateHandle实例

创建一个测试专用的Hilt模块,在ViewModel组件级别提供预填充参数的SavedStateHandle,覆盖默认的绑定逻辑:

@Module
@TestInstallIn(components = [ViewModelComponent::class])
object TestMyViewModelModule {
    @Provides
    fun provideSavedStateHandle(): SavedStateHandle {
        // 直接把需要的参数塞进去
        return SavedStateHandle(mapOf("myId" to 1L))
    }
}

测试类里正常调用即可,Hilt会自动用这个测试模块提供的SavedStateHandle:

@HiltAndroidTest
class MyTest {
    @get:Rule(order = 0)
    val hiltRule by lazy { HiltAndroidRule(this) }

    @get:Rule(order = 1)
    val test by lazy { createAndroidComposeRule<TestActivity>() }

    @Before
    fun setup() {
        hiltRule.inject()
    }

    @Test
    fun shouldRenderScreen() {
        test.setContent { MyScreen() }
        // 这里写你的断言逻辑
    }
}

如果项目里有专门给ViewModel提供依赖的模块,记得在@TestInstallIn的replaces参数里指定替换那个模块。

方案2:测试时用NavHost包裹目标页面

模拟真实的导航环境,在测试的setContent里创建NavHost并跳转到带参数的路由,Navigation会自动把参数注入SavedStateHandle:

@HiltAndroidTest
class MyTest {
    @get:Rule(order = 0)
    val hiltRule by lazy { HiltAndroidRule(this) }

    @get:Rule(order = 1)
    val test by lazy { createAndroidComposeRule<TestActivity>() }

    @Before
    fun setup() {
        hiltRule.inject()
    }

    @Test
    fun shouldRenderScreen() {
        test.setContent {
            val navController = rememberNavController()
            MyTheme {
                NavHost(navController = navController, startDestination = "dummy") {
                    // 先加载一个空页面,然后自动跳转到目标路由
                    composable("dummy") {
                        LaunchedEffect(Unit) {
                            navController.navigate("route/1")
                        }
                    }
                    // 跟应用里一样定义路由和参数
                    composable(
                        "route/{myId}",
                        arguments = listOf(navArgument("myId") { type = NavType.LongType })
                    ) {
                        MyScreen()
                    }
                }
            }
        }
        // 断言逻辑
    }
}

这个方案完全贴合真实场景,适合需要验证导航参数传递逻辑的测试。

方案3:给ViewModel加辅助注入构造(可选)

如果能修改ViewModel代码,用@AssistedInject配合工厂类,测试时就能灵活传入自定义的SavedStateHandle,同时保留Hilt的依赖注入:

@HiltViewModel
class MyViewModel @AssistedInject constructor(
    application: Application,
    // 其他Hilt注入的依赖
    ...
    @Assisted savedStateHandle: SavedStateHandle,
) : AndroidViewModel(application) {

    private val id = savedStateHandle.require<Long>("myId")

    // 定义辅助工厂类
    @AssistedFactory
    interface Factory {
        fun create(savedStateHandle: SavedStateHandle): MyViewModel
    }
}

测试时通过Hilt注入工厂,再创建带参数的ViewModel:

@HiltAndroidTest
class MyTest {
    @get:Rule(order = 0)
    val hiltRule by lazy { HiltAndroidRule(this) }

    @get:Rule(order = 1)
    val test by lazy { createAndroidComposeRule<TestActivity>() }

    @Inject
    lateinit var viewModelFactory: MyViewModel.Factory

    @Before
    fun setup() {
        hiltRule.inject()
    }

    @Test
    fun shouldRenderScreen() {
        val savedStateHandle = SavedStateHandle(mapOf("myId" to 1L))
        val viewModel = viewModelFactory.create(savedStateHandle)
        
        test.setContent {
            MyScreen(viewModel = viewModel)
        }
        // 断言逻辑
    }
}

这个方案适合需要频繁修改SavedStateHandle参数的测试场景,不用手动实例化一堆依赖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 17:43:15