Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 863
Alireza A. Ahmadi Avatar asked Jul 08 '18 05:07

Alireza A. Ahmadi


Video Answer


2 Answers

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).

like image 177
Martin Zeitler Avatar answered Oct 03 '22 06:10

Martin Zeitler


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)
    }
}
like image 35
James Olrog Avatar answered Oct 03 '22 05:10

James Olrog