I have two Fragments, one being a home fragment in my graph. The User will be navigated to the second fragment upon clicking a button. It works as expected by navigating the user to the second fragment and displaying the text. So the graph is good.
Now I wanted to write an instrumentation test.
@RunWith(AndroidJUnit4::class)
class TransitionTest {
@Test
fun testNavigationToSecondFragment() {
val navController = TestNavHostController(
ApplicationProvider.getApplicationContext())
navController.setGraph(R.navigation.my_graph) <--throws exception
// the rest of my test continues here
}
}
However, the line shown above to set the graph throws following exception:
IllegalStateException: Method addObserver must be called on the main thread.
My environment:
fragment_version = 1.2.5 nav_version = 2.3.1 espresso = 3.3.0
Does anyone have any idea what is going on and how to solve it?
You can add the Navigation Testing artifact to your project by adding the following dependency in your app module's build.gradle file: dependencies { def nav_version = "2.3.5" androidTestImplementation "androidx.navigation:navigation-testing:$nav_version" } Let’s say you are building a trivia game.
To test that the app properly navigates the user to the in_game screen when the user clicks Play, your test needs to verify that this fragment correctly moves the NavController to the R.id.in_game screen.
Note: when using Navigation 2.2.1 or earlier, it is recommended to use a mock NavController with Mockito and verify that the correct actions are taken rather than verify the NavController’s state.
Using a combination of FragmentScenario, Espresso , and TestNavHostController, you can recreate the conditions necessary to test this scenario, as shown in the following example: The above example creates an instance of TestNavHostController and assigns it to the fragment.
I wrapped the setGraph
function in runOnUiThread
as such, and the test passes. I will update the answer once I find out the real cause and better solution.
runOnUiThread {
navController.setGraph(R.navigation.my_graph)
}
I faced the same, so I will try to explain the root cause of this.
Starting from androidx.lifecycle:lifecycle-*:2.3.0-alpha06
. There is a behavior change in LifecycleRegistry
. LifecycleRegistry
now verifies that its methods are called on the main thread. Now remains another question unsolved whether the instrumentation tests are running in the MainThread? Apparently no otherwise it would have passed. It is using Instrumentation Thread where most of the tests run. As per documentation, you can use @UiThreadTest
or runOnUiThread
as in the top answer.
I started having this problem as well, after upgrading the package:
implementation 'androidx.activity:activity:1.2.0-alpha06'
to
implementation 'androidx.activity:activity:1.2.0-beta01'
In my code I am opening a Fragment in a background thread, and this seems to break with new package. The fragment adds an observer during lifecycle:
I think your workaround of wrapping the call to run on UI thread is the only plausible solution, since the observer may be added internally in your case as well.
There is one more solution to solve the issue by using this
InstrumentationRegistry.getInstrumentation().runOnMainSync {
navController.setGraph(R.navigation.my_graph)
}
Since @UiThreadTest
only work in @Test
, @Before
and @After
so if you have some common test function they might not work as expected
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With