google / dagger

A fast dependency injector for Android and Java.
https://dagger.dev
Apache License 2.0
17.45k stars 2.02k forks source link

Using Hilt in UITests #2521

Closed dumbfingers closed 3 years ago

dumbfingers commented 3 years ago

First thanks for providing Hilt, it is a very good and neat solution for Android development.

I have two questions for using Hilt in UITests:

My setup:

Followed the guide in https://dagger.dev/hilt/instrumentation-testing to have a custom runner and also HiltExt (https://developer.android.com/training/dependency-injection/hilt-testing#launchfragment)

Example Fragment A and the dialog both use the same viewmodel for data sharing.

@AndroidEntryPoint
class FragmentA : Fragment() {
    private val vm: FragmentAViewModel by viewModels()
    ...
    // FragmentA has a button to launch FragmentADialog
}
@AndroidEntryPoint
class FragmentADialog: BottomSheetDialogFragment() {
    private val vm: FragmentAViewModel by viewModels({ requireParentFragment() })
    ....
}

Then during the test:

@RunWith(AndroidJUnit4ClassRunner::class)
@HiltAndroidTest
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class FragmentATest {
        @get:Rule
        var hiltRule = HiltAndroidRule(this)

   // this would cause error when launching the dialog individually
    @Test
    fun test_launch_dialog() {
        launchFragmentInHiltContainer<FragmentADialog>(
            navHostController = navController
        ) {
            Navigation.setViewNavController(requireView(), navController)

            this.childFragmentManager
                .beginTransaction()
                .add(LanguageListDialogFragment(), "fragmentDialog")
                .commit()
        }
        ... other tests for the dialog content
    }
}

In the UITest, it has no effect when using the button to launch the dialog

Chang-Eric commented 3 years ago

I haven't used navigation much, but what launchInHiltContainer does is starts your fragment in a basically empty Hilt activity. It is possible you might need more configuration on your activity, so if that is the case, you would want to define your own Hilt activity with the extra navigation logic and use that in your test with ActivityScenario.

For mocking ViewModels, this is intentionally not supported. This is similar to how you can't really mock a fragment. You can use the various testing APIs though to replace the bindings underlying your ViewModel. If you really feel strongly about mocking your ViewModel logic, you would need to wrap it behind an abstraction that uses the ViewModel as an implementation detail, and then fake/mock it at that abstraction layer.