Code-Apprentice
Code-Apprentice

Reputation: 83557

Test Android Navigation starting from arbitrary location

I have the following navgraph with startDestination as card_list:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/card_list">

    <fragment
        android:id="@+id/card_list"
        android:name="bbct.android.common.fragment.BaseballCardList"
        android:label="@string/app_name"
        tools:layout="@layout/card_list">
        <action
            android:id="@+id/action_details"
            app:destination="@id/card_details" />
    </fragment>
    <fragment
        android:id="@+id/card_details"
        android:name="bbct.android.common.fragment.BaseballCardDetails"
        android:label="@string/card_details_title"
        tools:layout="@layout/card_details">
        <argument
            android:name="id"
            app:argType="long" />
    </fragment>
</navigation>

Now I want to write a test starting at card_details:

public class NavigationTest {
    @Test
    public void clickOnSaveNavigatesFromDetailsBackToList() {
        TestNavHostController navController = new TestNavHostController(ApplicationProvider.getApplicationContext());
        FragmentScenario<BaseballCardDetails> listScenario = FragmentScenario.launchInContainer(
            BaseballCardDetails.class,
            null,
            R.style.AppTheme
        );
        listScenario.onFragment(fragment -> {
            navController.setGraph(R.navigation.nav_graph);
            Navigation.setViewNavController(fragment.requireView(), navController);
        });
        assertThat(Objects.requireNonNull(navController.getCurrentDestination()).getId())
            .isEqualTo(R.id.card_details);
        BaseballCard card = new BaseballCard(
            true,
            "Mint",
            "Code Guru Apps",
            1993,
            "1",
            50000,
            1,
            "Code Guru",
            "Code Guru Devs",
            "Catcher"
        );
        BBCTTestUtil.sendKeysToCardDetails(card);
        Espresso.onView(ViewMatchers.withId(R.id.save_button)).perform(ViewActions.click());
        assertThat(Objects.requireNonNull(navController.getCurrentDestination()).getId())
            .isEqualTo(R.id.card_list);
    }
}

This fails on

        assertThat(Objects.requireNonNull(navController.getCurrentDestination()).getId())
            .isEqualTo(R.id.card_details);

because the TestNavHostController is on the startDestination of card_list.

The actual failure message is

expected: 2131296377

but was : 2131296378

at bbct.android.common.navigation.NavigationTest.clickOnSaveNavigatesFromDetailsBackToList(NavigationTest.java:116)

How do I force this test to start from the correct destination? Or is this not the correct way to write such a test? Am I supposed to write such a test by navigating to card_details first by performing the UI operations that a user would normally do?


Dependencies:

In case it matters, I have the following nav dependency versions:

    navVersion = '2.4.2'
    implementation "androidx.navigation:navigation-fragment:$navVersion"
    implementation "androidx.navigation:navigation-ui:$navVersion"

Upvotes: 1

Views: 566

Answers (1)

ianhanniballake
ianhanniballake

Reputation: 200020

As per the Testing Navigation guide:

Just like a real NavController, you must call setGraph to initialize the TestNavHostController. In this example, the fragment being tested was the start destination of our graph. TestNavHostController provides a setCurrentDestination method that allows you to set the current destination (and optionally, arguments for that destination) so that the NavController is in the correct state before your test begins.

So add a call to setCurrentDestination to initialize your test on a particular destination:

listScenario.onFragment(fragment -> {
    navController.setGraph(R.navigation.nav_graph);

    // Create arguments using Safe Args generated classes
    // or construct the Bundle directly
    BaseballCardDetailsArgs args = new BaseballCardDetailsArgs.Builder(0).build();
    Bundle arguments = args.toBundle();

    // Then set the current destination to your card_details
    navController.setCurrentDestination(R.id.card_details, arguments);

    Navigation.setViewNavController(fragment.requireView(), navController);
});

Upvotes: 2

Related Questions