我有一个片段,其 ViewModel 实例由组件提供,范围仅限于主活动的导航图。 Dagger 应用程序组件被注入到单个主活动中,并作为惰性委托变量抓取到片段内。
private val myViewModel: MyViewModel by lazy {
ViewModelProvider(findNavController().getBackStackEntry(R.id.nav_graph), Factory(this) { stateHandle ->
(activity as MainActivity).myComponent.myViewModel()
.create(stateHandle)}).get(MyViewModel::class.java)
}
ViewModelProvider 的 ViewModelProvider.Factory 参数使用 MyViewModel 类中定义的 Factory 接口的 create 方法;该接口使用@AssistedFactory进行注释,而savedStateHandle是在MyViewModel构造函数中使用@AssistInject进行注释。
class MyViewModel @AssistedInject constructor(
private val myRepository: MyRepository,
@Assisted val savedStateHandle: SavedStateHandle
) : ViewModel() {
@AssistedFactory
interface MyViewModelFactory : ViewModelProvider.Factory {
fun create(savedStateHandle: SavedStateHandle) : MyViewModel
}
//Other ViewModel code
}
应用程序运行良好,Dagger 应用程序组件通过其惰性委托获取我提供的 MyViewModel 实例,但现在当我尝试编写一个仪器测试来测试 Fragment 本身的一些 ui 字段时,我遇到了空点异常。具体来说:
java.lang.NullPointerException: Parameter specified as non-null is null: method androidx.lifecycle.ViewModelProvider.<init>, parameter owner
at androidx.lifecycle.ViewModelProvider.<init>(Unknown Source:2)
这告诉我所有者参数,
findNavController().getBackStackEntry(R.id.nav_graph)
NPE 失败,但我不知道如何解决它,以便我的测试可以编译和执行。
这是到目前为止我的测试代码,请帮助我在这里缺少的地方,否则我可能会尝试删除 Dagger 并实例化所有内容以开始测试,回想起来这听起来像是倒退。我什至不确定我是否正确编写了这个测试场景,因为我正在尝试通过各种帮助论坛和此网站传播的解决方案大杂烩,以解决我的编译时问题和构建失败的问题。
@RunWith(AndroidJUnit4::class)
class MyFragmentTest {
@get:Rule
var activityRule = ActivityScenarioRule(MainActivity::class.java)
private lateinit var scenario: FragmentScenario<MyFragment>
private lateinit var binding: FragmentMyBinding
private lateinit var mockNavController : NavController
@Test
fun setUp() {
mockNavController = mock(NavController::class.java)
scenario = launchFragmentInContainer<MyFragment> {
MyFragment().also { fragment ->
fragment.viewLifecycleOwnerLiveData.observeForever { viewLifecycleOwner ->
if (viewLifecycleOwner != null) {
// The fragment’s view has just been created
mockNavController.setGraph(R.navigation.nav_graph)
Navigation.setViewNavController(fragment.requireView(), mockNavController)
}
}
}
}
}
任何正确方向的帮助或指导将不胜感激,因为我已经被困在这里一段时间了。
用我如何让它工作来回答我自己的问题:使用 TestNavHostController。
@Test
fun setUp() {
mockNavController = TestNavHostController(ApplicationProvider.getApplicationContext())
scenario = launchFragmentInContainer<MyFragment> {
MyFragment().also { fragment ->
fragment.viewLifecycleOwnerLiveData.observeForever { viewLifecycleOwner ->
if (viewLifecycleOwner != null) {
// The fragment’s view has just been created
fragment.requireActivity().runOnUiThread {
mockNavController.setViewModelStore(ViewModelStore())
mockNavController.setGraph(R.navigation.base_nav_graph)
Navigation.setViewNavController(fragment.requireView(), mockNavController)
}
}
}
}
}
这样我就解决了实例化视图模型的问题。