I have a fragment whose instance of ViewModel is provided by a component, scoped to the navigation graph of Main Activity. The Dagger app component was injected into the single Main Activity and grabbed inside the the fragment as a lazy delegated variable.
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)
}
The ViewModelProvider.Factory parameter of ViewModelProvider is using a create method from a Factory interface defined in MyViewModel class; this interface is annotated with @AssistedFactory whereas the savedStateHandle is @AssistInject annotated in the MyViewModel constructor.
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
}
App runs fine with the Dagger app component getting my provided instances of MyViewModel through its lazy delegates, but now when I’m trying to write an instrumented test to test some ui fields on the Fragment itself, I’m getting null point exception. Specifically:
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)
This tells me that the owner parameter, findNavController().getBackStackEntry(R.id.nav_graph)
is failing with NPE, but I don’t know how to go about resolving it so my test can compile and execute.
Here is my test code so far, please help where I’m missing here, else I might try removing Dagger and instantiating everything just to get testing going, which sounds backwards in retrospect. I’m not even sure if I’m writing this test scenario correctly, as I’m trying a hodge podge of solutions spread through various help forums and this site for solutions to my compile time issues and failure to build.
@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)
}
}
}
}
}
Any help or guidance in the right direction would be greatly appreciated as I’ve been stuck for a while here. Thank you.