如何在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
相关产品推荐
相关产品推荐

