How to Automatically Test Jetpack Compose Navigation

Navigation is a core functionality of all mobile apps, and the tedious work lies in manually testing every possible navigation path from a single screen, which can be both time-consuming and error-prone. One of the most useful new features is type-safe navigation, introduced in Navigation Component 2.8.0 through the use of serializable objects. However, integrating this feature requires some migration steps, which we will cover in this guide. This migration process will also illustrate the benefits of having automated navigation tests in place. However, integrating this new feature may require small or significant changes, depending on the app’s current implementation.
In this article, we will:
- Implement navigation using the “old way.”
- Develop navigation tests.
- Migrate to the “new way” with type-safe navigation.
Note: Even though type-safe navigation is available, the “old way” is still supported in newer library versions. For this guide, we will use Navigation Component 2.8.6 for both approaches.
Implementing the navigation
Dependencies
Ensure the following dependencies are added to your project. The navigation testing library is particularly important as it provides TestNavHostController
for testing.
Defining Navigation Routes
Navigation routes are defined as string constants:
Setting Up the Navigation Graph
Each composable screen is associated with a route in the navigation graph. Additionally, callback functions are defined to handle navigation:
Integrating with the Scaffold Menu
These routes are also used to populate the navigation menu in the Scaffold
. In this example, the Adaptive Library automatically handles menu type and positioning. If you want more information, refer to this article.
Testing the navigation
Now, let’s write tests to verify that navigation works correctly from both the menu and screen callbacks.
Note: Within composeTestRule.setContent
, string resources are used instead of test tags to identify UI nodes, such as buttons.
By running this test, we can confirm that navigation is working correctly!

Migrating to Type-Safe Navigation
As mentioned earlier, navigation will be upgraded to leverage type-safe features.
Step 1: Enable Serialization Plugin
Ensure the serialization plugin is included in build.gradle.kts
(if not already available):
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
plugins {
alias(libs.plugins.kotlin.serialization) apply false
...
}
Step 2: Apply Plugin in Module
Ensure the serialization plugin is applied in the application module and any other module where it is needed.
plugins {
alias(libs.plugins.kotlin.serialization)
...
}
Step 3: Migrate Routes to Serializable Objects
Instead of using string constants, define routes as serializable objects:
Step 4: Update the Navigation Graph
Modify the navigation graph to use serializable objects:
Step 5: Update the Scaffold Menu
Ensure the menu items use the updated navigation approach:
Step 6: Update Navigation Assertions in Tests
Instead of currentDestination
, use currentBackStackEntry
:
// Change this
assertTrue { navController.currentDestination?.route == NavigationRoutes.BOOK_SEARCH }
// For this
assertTrue { navController.currentBackStackEntry?.destination?.hasRoute<BookSearch>() == true }
Step 7: Modify setCurrentDestination
setCurrentDestination
in TestNavHostController
does not support serializable objects, so navigation functions must be used instead:
// Change this
composeTestRule.runOnUiThread {
navController.setCurrentDestination(NavigationRoutes.BOOK_SEARCH)
}
// For this
composeTestRule.runOnUiThread {
navController.navigate(BookSearch)
}
Full Test Code
Here is the complete test code to verify navigation:
Final Verification
After applying these changes, re-run the tests to ensure that navigation still functions correctly.

Closing
If you found this article helpful or interesting, please give it a clap and consider subscribing for more content! I’d love to hear your thoughts! Your feedback and insights are always welcome, as I’m eager to learn, collaborate, and grow with other developers in the community.
Have any questions? Feel free to reach out!
You can also follow me on Medium or LinkedIn for more insightful articles and updates. Let’s stay connected!