Search code examples
unit-testingxamarin.androidxamarin.iosmoqmvvmcross

Navigation Unit Testing in MvvmCross


Trying to unit test the navigation in one of my command calls into a private method. Just trying to test if the navigation request has been made as a result of this command execution.

There's the old documentation; https://www.mvvmcross.com/documentation/fundamentals/testing

This documentation does not factor in new async based calls as far as I found; For example IMvxMainThreadAsyncDispatcher Either we need to implement two ExecuteOnMainThreadAsync methods or inherit from MvxMainThreadAsyncDispatcher in MockDispatcher.

Also need to add IMvxMainThreadAsyncDispatcher in IoC registration.

var mockDispatcher = new MockDispatcher();

...

...

Ioc.RegisterSingleton<IMvxMainThreadAsyncDispatcher>(MockDispatcher);

So almost all tests work except navigation call requests. Below method inside MockDispatcher never gets called so I can't check request counts;

public async Task<bool> ShowViewModel(MvxViewModelRequest request)
{
     Requests.Add(request);
     return true;
}

Anybody has a working code that would mock and gets this Request called or in some other form? IMvxMainThreadDispatcher is being set as absolute, and navigation calls are not done with ShowViewModel<>() anymore in MVVMCross, it's done by calling navigationService.Navigate();


Solution

  • Well, I have found the solution to my question... The ShowViewModel is called when navigation service is properly mocked. I have found a piece of code on GitHub from MvvmCross's own repo on how they do tests for navigation. My next challenge would be to mock the destination viewModel but that's separate and below code doesn't cover that. Previously I had a very basic IMvxNavigationService mock.

    var mockLocator = new Mock<IMvxViewModelLocator>();
    mockLocator.Setup(
                    m => m.Load(It.IsAny<Type>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxNavigateEventArgs>())).Returns(() => new FakeViewModel());
    mockLocator.Setup(
                    m => m.Reload(It.IsAny<IMvxViewModel>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxNavigateEventArgs>())).Returns(() => new FakeViewModel());
    
    var mockCollection = new Mock<IMvxViewModelLocatorCollection>();
    mockCollection.Setup(m => m.FindViewModelLocator(It.IsAny<MvxViewModelRequest>()))
                    .Returns(() => mockLocator.Object);
    
    Ioc.RegisterSingleton(mockLocator.Object);
    
    var loader = new MvxViewModelLoader(mockCollection.Object);
    
    _navigationService = new MvxNavigationService(null, loader)
    {
          ViewDispatcher = MockDispatcher,
    };
    _navigationService.LoadRoutes(new[] { typeof(YourViewModelTestClass).Assembly });
    Ioc.RegisterSingleton<IMvxNavigationService>(_navigationService);