0

I have created a LoginFragment which navigates to another one when a login is successful using the Navigation Architecture Component. Works fine but the test for it is failing. It is quite a close mirror of the GithubBrowser sample and my test is very similar to the clickRepo test here, except I am testing that a new page is loaded when a successful Resource is posted instead of upon a click.

Error:

Wanted but not invoked:
navController.navigate(
app.ui.login.LoginFragmentDirections$ShowSelectMerchant@377f3c27
);

LoginFragmentTest.kt

@Test
fun success(){
    val user = TestUtil.createUser(email)

    userData.postValue(Resource.success(user))

    System.out.println("Test NavController Hash: " + testFragment.navController.hashCode())
            verify(testFragment.navController).navigate(LoginFragmentDirections.showSelectMerchant().matcher())

}

LoginFragment.kt

override fun openSelectMerchantFragment() {
    System.out.println("Real NavController Hash: " + navController().hashCode())
    navController().navigate(LoginFragmentDirections.showSelectMerchant())
}

openSelectMerchantFragment is called when a successful Resource is posted to the live userData. Is there an obvious reason why the test is failing?

I can see that the test fragment's mocked nav controller and the one that is called in the fragment itself have the same hash value so I am pretty sure it's not a problem with how I have mocked the activity, fragment or nav controller. I can also obviously see that the navigate() function is definitely called.

Daniel Wilson
  • 18,838
  • 12
  • 85
  • 135
  • Are you using [CountingTaskExecutorRule](https://developer.android.com/reference/android/arch/core/executor/testing/CountingTaskExecutorRule)? That's necessary if you're using `postValue` – ianhanniballake Sep 27 '18 at 20:10
  • Thanks Ian but that ain't my problem. I had forgotten the `TaskExecutorWithIdlingResourceRule` rule, but interestingly my other tests which use `postValue` pass without it, as do _all_ of the tests in the GithubBrowserSample's entire `UserFragmentTest`. So that's another question in itself, this is going to require some digging :) – Daniel Wilson Oct 01 '18 at 18:05
  • Thought I had figured it out in that I was missing a call to `matcher()` which the sample uses to mock the Nav arguments but that hasn't made any difference either (I have no args anyway). – Daniel Wilson Oct 01 '18 at 18:45
  • Retitled the question so it is more apt - calling drainTasks to allow the posting to finish seems to be the answer. Thanks! – Daniel Wilson Oct 04 '18 at 17:01

1 Answers1

1

The problem here was a simple one of testing LiveData as Ian suggests. It is not enough to add a TaskExecutorWithIdlingResourceRule, but drainTasks() must be called to allow the data posting to complete. I can't answer why it works in the sample's entire test class without any call to drainTasks, my best guess is that the posting in those tests simply complete fast enough for it to not be an issue. I think it is good practice to always call drainTasks whenever a live data is posted to.

Final result:

@Test
fun success(){
    val user = TestUtil.createUser(email)

    userData.postValue(Resource.success(user))
    executorRule.drainTasks(1, TimeUnit.SECONDS)

    verify(testFragment.navController).navigate(LoginFragmentDirections.showSelectMerchant().matcher())
}
Daniel Wilson
  • 18,838
  • 12
  • 85
  • 135
  • Came across the same problem today while following along with the github repo sample. Strangely enough most of my `postValue` tests work without calling drain tasks, it was only the navigation tests that didn't. Feel free to mark this as correct answer – Joe Maher Jul 05 '19 at 03:48