测试导航组件:“没有NavController”

时间:2019-11-20 08:58:17

标签: android android-espresso android-testing android-architecture-components android-viewmodel

我正在实施Espresso测试。我正在使用范围为NavGraph的{​​{1}}片段。问题是当我尝试测试ViewModel时得到了Fragment,因为IllegalStateException没有设置Fragment。我该如何解决这个问题?

NavController

测试类:

class MyFragment : Fragment(), Injectable {

    private val viewModel by navGraphViewModels<MyViewModel>(R.id.scoped_graph){
        viewModelFactory
   }

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    //Other stuff
}

我得到的异常:

class FragmentTest {

    class TestMyFragment: MyFragment(){
        val navMock = mock<NavController>()

        override fun getNavController(): NavController {
            return navMock
        }
    }

    @Mock
    private lateinit var viewModel: MyViewModel
    private lateinit var scenario: FragmentScenario<TestMyFragment>

    @Before
    fun prepareTest(){
        MockitoAnnotations.initMocks(this)

    scenario = launchFragmentInContainer<TestMyFragment>(themeResId = R.style.Theme_AppCompat){
        TestMyFragment().apply {
            viewModelFactory = ViewModelUtil.createFor(viewModel)
        }
    }

    // My test
}

2 个答案:

答案 0 :(得分:2)

docs中可以看到,这是建议的方法:

// Create a mock NavController
val mockNavController = mock(NavController::class.java)

scenario = launchFragmentInContainer<TestMyFragment>(themeResId = R.style.Theme_AppCompat) {
    TestMyFragment().also { fragment ->     
        // In addition to returning a new instance of our Fragment,
        // get a callback whenever the fragment’s view is created
        // or destroyed so that we can set the mock NavController
        fragment.viewLifecycleOwnerLiveData.observeForever { viewLifecycleOwner ->
            if (viewLifecycleOwner != null) {
                // The fragment’s view has just been created
                Navigation.setViewNavController(fragment.requireView(), mockNavController)
            }
        }
    }
}

此后,您可以像这样对模拟的mockNavController进行验证:

verify(mockNavController).navigate(SearchFragmentDirections.showRepo("foo", "bar"))

请参阅architecture components sample以供参考。


docs中也提到了另一种方法:

    // Create a graphical FragmentScenario for the TitleScreen
    val titleScenario = launchFragmentInContainer<TitleScreen>()

    // Set the NavController property on the fragment
    titleScenario.onFragment { fragment ->
        Navigation.setViewNavController(fragment.requireView(), mockNavController)
    }

如果直到NavController(包括)与onViewCreated()之间发生交互,此方法将不起作用。使用这种方法onFragment()会在生命周期中将模拟NavController设置得太晚,从而导致findNavController()调用失败。作为一种适用于所有情况的统一方法,我建议使用第一种方法。

答案 1 :(得分:1)

您缺少设置NavController

testFragmentScenario.onFragment {
            Navigation.setViewNavController(it.requireView(), mockNavController)
        }