Jonas Reif
Jonas Reif

Reputation: 324

Unit test Dagger Fragments with FragmentScenario

I am trying to test fragment interactions using the Android Jetpack Navigation Component and the fragment-testing library. My app is using java + Dagger2 as DI.

To test the navigation I have created a JUnit test:

    @Test
    public void testNavigationToLoginFragment() {
        // Create a mock NavController
        NavController mockNavController = mock(NavController.class);

        // Create a graphical FragmentScenario for the Intro Fragment
        FragmentScenario<IntroFragment> introFragmentScenario = FragmentScenario.launchInContainer(IntroFragment.class);

        // Set the NavController property on the fragment
        introFragmentScenario.onFragment(fragment ->
                Navigation.setViewNavController(fragment.requireView(), mockNavController)
        );

        // Verify that performing a click prompts the correct Navigation action
        onView(withId(R.id.button_login)).perform(click());
        verify(mockNavController).navigate(R.id.action_intro_fragment_to_login_fragment);
    }

Whenever I run the test I get following error:

java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: No injector factory bound for Class<XXX>

How can I inject my fragment there? Is it possible to use DaggerFragments with FragmentScenario?

IntroFragment

public class IntroFragment extends DaggerFragment{
    @Inject
    CreateQuoteRecyclerViewAdapter createQuoteRecyclerViewAdapter;

    @Inject
    public ViewModelProvider.Factory factory;

    @inject 
    public MyViewModel viewModel;
    .....

}

MyViewModel.java

class CreateOrSignInViewModel extends BaseViewModel() {
  @Inject
  public  CreateOrSignInBindingState state;


 ......
}

Upvotes: 21

Views: 1935

Answers (1)

azizbekian
azizbekian

Reputation: 62189

Declare following function within test class:

@Config(application = TestApp::class)
@RunWith(AndroidJUnit4::class)
@LooperMode(LooperMode.Mode.PAUSED)
class MyFragmentTest {

    private val createQuoteRecyclerViewAdapter: CreateQuoteRecyclerViewAdapter = mock()
    private val viewModel: MyViewModel = mock()

    private fun launchFragment(): FragmentScenario<MyFragment> {
      return launchFragmentInContainer(factory = object : FragmentFactory() {
        override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
          return MyFragment().apply {
            createQuoteRecyclerViewAdapter = [email protected]
            viewModel = [email protected]
            // assign other deps here as per your needs
          }
        }
      }, themeResId = R.style.AppTheme)
    }

}

Where TestApp is declared as such:


    class TestApp : Application()

This might be needed in order to refrain from error, which will happen in your custom application class's onCreate() method, where Dagger tree is being constructed.

After having this setup you can start writing your unit test:

@Test
fun `navigation to login screen is correctly performed`() {
    val navController: NavController = mock()

    val scenario = launchFragment()
    scenario.onFragment {
        Navigation.setViewNavController(it.requireView(), navController)
    }

    onView(withId(R.id.button_login)).perform(click())
    verify(mockNavController).navigate(R.id.action_intro_fragment_to_login_fragment)
}

Upvotes: 4

Related Questions