I'm currently new to testing, so I decided to start off with some basic stuff.
I handle all my navigations from a DrawerLayout that is connected to an Activity.
So for my testing I launch an ActivityScenarioRule, create a testNavController object and then I set this testNavController to the current view that handles the navigation (The container fragment).
So the test consists on opening the drawer, clicking on menu item(Will navigate to a fragment) and therefore check if navigated to the fragment.
Then I check if that happened, but the testNavController stays on the same destination which is weird because it performs the click, so I decided to check the navController (The real one inside the activity), and it shows me that navigated to the correct fragment.
Here's the needed code:
@LargeTest
@RunWith(AndroidJUnit4::class)
class MapsActivityTest {
@get:Rule
var activityScenarioRule = ActivityScenarioRule(MapsActivity::class.java)
@Test
fun clickOnDrawerMaps_NavigateToAboutAppFragment() {
//Create TestNavHostController
val testNavController = TestNavHostController(ApplicationProvider.getApplicationContext())
UiThreadStatement.runOnUiThread { // This needed because it throws a exception that method addObserver must be called in main thread
testNavController.setGraph(R.navigation.nav_graph)
}
val scenario = activityScenarioRule.scenario
var navcontroller : NavController? = null
scenario.onActivity {mapsActivity ->
navcontroller = mapsActivity.navController //Get the real navController just to debug
mapsActivity.navController = testNavController //Set the test navController
Navigation.setViewNavController(mapsActivity.binding.containerFragment, testNavController)
}
onView(withId(R.id.drawerLayout)).perform(DrawerActions.open()).check(matches(isOpen()))
onView(withId(R.id.aboutAppFragment)).perform(click())
assertThat(testNavController.currentDestination?.id).isEqualTo(R.id.aboutAppFragment)
}
}
In the example they use a Fragment, which they set the fragment.requireView() on the launch of the fragment, but I think it's exactly the same.
What am I doing wrong here?
When you use ActivityScenario
(or ActivityScenarioRule
), your activity is brought all the way up to the resumed state before any onActivity
calls are made. This means that your real NavController has already been created and used when you call setupWithNavController
. This is why your call to setViewNavController()
has no effect.
For these types of integration tests (where you have a real NavController
), you should not use TestNavHostController
.
As per the Test Navigation guide, TestNavHostController
is designed for unit tests where you do not have any real NavController
at all, such as when testing one fragment in isolation.