Reputation: 99
I have an existing fragment class as part of a navigation graph component and uses Dagger to create instances of ViewModels scoped to the nav graph. However, I have a few questions about FragmentScenario and how it works.
But first, a basic setup function I want to run to establish the FragmentScenario before every UI Test.
private lateinit var scenario: FragmentScenario<MyFragment>
private lateinit var mockNavController : NavController
//Annotation to be marked as @Before later
@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)
}
}
}
}
}
}
In MyFragment
class, I have lazy vals of my View Models that hold the functions to execute network request calls with Retrofit. In actual app environment, it is intentional to run network request at fragment onViewCreated based on conditions in flags inside the view model; the conditions are set in the view model inside the host activity.
From what I see, FragmentScenario environment also does the same calls as if I'm running the app. Aside from running into headaches caused by lazy instantiations of LiveData inside the viewmodel (Think "setValue cannot be ran on a background thread") when running network calls on an IO dispatched CoroutineScope because I need some request params for the call from those lazy LiveData vals, is this intended behavior for a FragmentScenario to execute network calls even in an instrumentedTest environment? I would prefer to prevent this without any conditionals if possible.
I'm finding that with FragmentScenario, I'm starting to change the visibility of my code from private to public just to access some of the variables and functions. Is this also expected when trying to make instrumented tests?
In the topic of view model testing, is there a possibility to mock my view model functions or class itself? My view model definition is as follows:
class MyViewModel @AssistedInject constructor(
private val repository : MyRepository,
@Assisted val savedStateHandle: SavedStateHandle
) : ViewModel()
//and the factory
@AssistedFactory
interface MyViewModelFactory {
fun create(savedStateHandle: SavedStateHandle) : MyViewModel
}
//lazy val
val myViewModel: MyViewModel by lazy {
ViewModelProvider(findNavController().getBackStackEntry(R.id.base_nav_graph), Factory(this) { stateHandle ->
appComponent.myViewModel()
.create(stateHandle)}).get(MyViewModel::class.java)
}
AppComponent being the subcomponent in the Dagger Graph where the view model's factory is injected into the fragment. If I can mock the ViewModel for the conditions and network call functions, it might make testing this easier if I want to isolate test the request with mock data.
Upvotes: 0
Views: 31