Reputation: 2812
A lot of our ViewModel-Unit-Tests create a ViewModel in the Arrange-Phase, call OnNavigatedTo() in the Act-Phase and Assert some stuff that should have happened after OnNavigatedTo finished (e.g. some Properties contain certain values).
When the implementation of OnNavigatedTo() contains asynchronious method calls (like loading data from the backend), we have a problem because OnNavigatedTo returns void and we can not await it's completion.
In our unit tests, we mock the calls to the backends, so they return immediately and the problem almost never shows up. However we had a case where exactly this scenario caused an issue on the build server (running on Linux) and i suspect this always creates a kind of race-condition which just works by accident most of the time.
One workaround we came up with was to provide a public method returning a Task which contains the implementation of OnNavigatedTo and is used by the unit tests but it's generally no good idea to extend the public API surface of the system under test just for the sake of testing.
Moving all the code to IInitializeAsync.InitializeAsync is in my opinion not an option as well as those 2 lifecycle hooks aren't equivalent and InitializeAsync might not get called on every page navigation.
So my question is: How can we reliably unit test code in INavigationAware.OnNavigatedTo which makes async calls?
Upvotes: 2
Views: 522
Reputation: 10883
One workaround we came up with was to provide a public method returning a Task which contains the implementation of
OnNavigatedTo
and is used by the unit tests
I'd do exactly this, except that I'd use internal
(with InternalsVisibleTo
if the fixture must be in another assembly) or even private
(if you can make the fixture a nested class).
Alternatively, define your own INavigatedToAsyncForTest
(with methods that return Task
) and implement it explicitly to limit the discoverability of these methods.
but it's generally no good idea to extend the public API surface of the system under test just for the sake of testing.
True, but making the method internal
screams "this is not public API", especially if the type itself is internal
(which view models should be, unless you have to put your views in different assembly), so you can somewhat mitigate this.
Upvotes: 1