使用Hilt与Navigation组件时Fragment UI测试运行报错如何解决
问题解决方法
- 错误根本原因:
TestNavHostController的导航状态修改方法(包括setGraph、setCurrentDestination)必须在Android主线程执行,当前代码直接在launchFragmentInHiltContainer的回调中调用这些方法,回调运行线程不为主线程,因此抛出异常。
步骤1:修改导航控制器的调用逻辑
将navController的状态修改操作放到主线程执行,修改后的launchWelcomeFragment方法如下:
private fun launchWelcomeFragment() { launchFragmentInHiltContainer<WelcomeFragment> { // 导航状态修改操作必须运行在主线程 requireActivity().runOnUiThread { navController.setGraph(R.navigation.nav_graph) navController.setCurrentDestination(R.id.welcomeFragment) } viewLifecycleOwnerLiveData.observeForever { viewLifecycleOwner -> if (viewLifecycleOwner != null) { Navigation.setViewNavController(requireView(), navController) } } } }
步骤2(可选推荐):添加主线程调度规则
在测试类中添加主线程规则,确保协程测试场景下所有操作默认运行在主线程:
@get:Rule val mainDispatcherRule = MainDispatcherRule()
补充:如果你的项目中没有现成的
MainDispatcherRule,可以用以下代码实现:@OptIn(ExperimentalCoroutinesApi::class) class MainDispatcherRule( val testDispatcher: TestDispatcher = UnconfinedTestDispatcher() ) : TestWatcher() { override fun starting(description: Description) { super.starting(description) Dispatchers.setMain(testDispatcher) } override fun finished(description: Description) { super.finished(description) Dispatchers.resetMain() } }
步骤3:检查依赖版本一致性
确保你项目中navigation-testing测试依赖的版本号,和项目主模块使用的Navigation Components版本号完全一致,避免版本不兼容导致的异常。
修改后的完整测试类代码
@HiltAndroidTest @RunWith(AndroidJUnit4::class) class WelcomeFragmentTest { @get:Rule val hiltRule = HiltAndroidRule(this) @get:Rule val mainDispatcherRule = MainDispatcherRule() private val navController = TestNavHostController(ApplicationProvider.getApplicationContext()) @Before fun setUp(){ hiltRule.inject() } @Test fun `testFragmentinits`(){ launchWelcomeFragment() } private fun launchWelcomeFragment() { launchFragmentInHiltContainer<WelcomeFragment> { requireActivity().runOnUiThread { navController.setGraph(R.navigation.nav_graph) navController.setCurrentDestination(R.id.welcomeFragment) } viewLifecycleOwnerLiveData.observeForever { viewLifecycleOwner -> if (viewLifecycleOwner != null) { Navigation.setViewNavController(requireView(), navController) } } } } }
内容的提问来源于stack exchange,提问作者Behruz Bahromzoda
相关产品推荐
相关产品推荐

