Alireza Ahmadi
Alireza Ahmadi

Reputation: 5348

How to write tests for Android Navigation Controller

I'm using the new Navigation Controller which is currently in alpha. It works fine but I can't find any documentation or sample app to see how testing is done. Also google published android.arch.navigation:navigation-testing library for testing navigation but again there is no documentation.

Any help or suggestion will be appreciated.

Upvotes: 22

Views: 9780

Answers (2)

Martin Zeitler
Martin Zeitler

Reputation: 76779

Here a recent example of mine with a FragmentScenario and TestNavHostController:

dependencies {
    androidTestImplementation "androidx.navigation:navigation-testing:2.3.5"
    implementation "androidx.navigation:navigation-fragment:2.3.5"
    implementation "androidx.navigation:navigation-runtime:2.3.5"
}

And the instrumented test:

/**
 * Instrumented Navigation Test
 * @author Martin Zeitler
 */
@RunWith(AndroidJUnit4.class)
public class NavControllerTest {

    @IdRes
    private final int theme = R.style.Theme_AppCompat_DayNight;

    @Test
    public void testHomeFragmentToLoginFragment() {
        
        Bundle args = new Bundle();
        FragmentScenario<HomeFragment> navhostScenario = FragmentScenario.launchInContainer(HomeFragment.class, args, theme, Lifecycle.State.STARTED);

        navhostScenario.onFragment(fragment -> {

            // Create a NavController and set the NavController property on the fragment
            assertNotNull(fragment.getActivity());
            TestNavHostController navController = new TestNavHostController(fragment.getActivity());
            fragment.getActivity().runOnUiThread(() -> navController.setGraph(R.navigation.nav_graph));
            Navigation.setViewNavController(fragment.requireView(), navController);

            // Then navigate
            navController.navigate(R.id.action_homeFragment_to_loginFragment);
            NavDestination destination = navController.getCurrentDestination();
            assertNotNull(destination);
            assertEquals(destination.getId(), R.id.loginFragment);
        });
    }
}

Such -> Lambda expressions require compileOptions.sourceCompatibility JavaVersion.VERSION_1_8 set in the build.gradle. And one can obtain the Activity from fragment.getActivity() (beware, it's not the usual one).

Upvotes: 12

James Olrog
James Olrog

Reputation: 435

The official android documentation currently provides some detail but there aren’t a lot of examples.

In your test, you can provide a mock NavController using Mockito and can use it to verify your app's interactions.

For example, to test that the app properly navigates the user to a specific screen when the user clicks a button, your test needs to verify that this fragment invokes NavController.navigate() with the desired action.

Using a combination of FragmentScenario, Espresso, and Mockito, you can recreate the conditions necessary to test this scenario, as shown below:

@RunWith(AndroidJUnit4::class)
class FirstScreenTest {

    @Test
    fun testNavigationToSecondScreen() {
        // Create a mock NavController
        val mockNavController = mock(NavController::class.java)

        // Create a graphical FragmentScenario for the FirstScreen
        val firstScenario = launchFragmentInContainer<FirstScreen>()

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

        // Verify that performing a click prompts the correct Navigation action
        onView(ViewMatchers.withId(R.id.button)).perform(ViewActions.click())
        verify(mockNavController).navigate(R.id.action_first_screen_to_second_screen)
    }
}

Upvotes: 8

Related Questions