Unit test Prism navigation

2019-03-01 05:07发布

问题:

I'm creating an application using Prism and Xamarin Forms. I want to unit-test my view models.

I have a command that navigate to a page. I want to assert that the navigation occured to the right page after that command has been called.

I guess that I need to replicate the code found in the Prism/Source/Xamarin/Prism.Forms.Tests/Navigation/PageNavigationServiceFixture.cs file.

For example, look at this method (coming from the PageNavigationServiceFixture class) :

    public async void Navigate_ToContentPage_ByName()
    {
        var navigationService = new PageNavigationServiceMock(_container, _applicationProvider, _loggerFacade);
        var rootPage = new Xamarin.Forms.ContentPage();
        ((IPageAware)navigationService).Page = rootPage;

        await navigationService.NavigateAsync("ContentPage");

        Assert.True(rootPage.Navigation.ModalStack.Count == 1);
        Assert.IsType(typeof(ContentPageMock), rootPage.Navigation.ModalStack[0]);
    }

It creates a navigation service, navigate to a page and then assert that the page has changed.

Now here is my test class :

[TestClass]
public class MenuPrincipalViewModelTests
{
    [TestMethod]
    public void AProposCommand()
    {
        INavigationService navigationService = new PageNavigationServiceMock(_container, _applicationProvider, _loggerFacade);

        // 1: Here I need to initialize my navigation service to my MasterDetailPage

        MenuPrincipalPageViewModel viewModel = new MenuPrincipalPageViewModel(navigationService);

        viewModel.AProposCommand.Execute();

        // 2: Here I want to assert that the new navigation stack is MenuPrincipalPage/ContenuPage/AProposPage
    }

    PageNavigationContainerMock _container;
    IApplicationProvider _applicationProvider;
    ILoggerFacade _loggerFacade;

    public MenuPrincipalViewModelTests()
    {
        _container = new PageNavigationContainerMock();

        _container.Register("PageMock", typeof(PageMock));

        _container.Register("ContentPage", typeof(ContentPageMock));
        _container.Register(typeof(ContentPageMockViewModel).FullName, typeof(ContentPageMock));

        _container.Register("NavigationPage", typeof(NavigationPageMock));
        _container.Register("NavigationPage-Empty", typeof(NavigationPageEmptyMock));
        _container.Register("NavigationPageWithStack", typeof(NavigationPageWithStackMock));
        _container.Register("NavigationPageWithStackNoMatch", typeof(NavigationPageWithStackNoMatchMock));

        _container.Register("MasterDetailPage", typeof(MasterDetailPageMock));
        _container.Register("MasterDetailPage-Empty", typeof(MasterDetailPageEmptyMock));

        _container.Register("TabbedPage", typeof(TabbedPageMock));
        _container.Register("CarouselPage", typeof(CarouselPageMock));

        _container.Register("MenuPrincipalPage", typeof(MenuPrincipalPage));
        _container.Register("ContenuPage", typeof(ContenuPage));
        _container.Register("ClubHousePage", typeof(ClubHousePage));
        _container.Register("AProposPage", typeof(AProposPage));

        _applicationProvider = new ApplicationProviderMock();
        _loggerFacade = new EmptyLogger();
    }
}

The constructor was copied from PageNavigationServiceFixture. I now need to implement the AProposCommand test and I have 2 questions (noted in the code)

How to initialize the navigation service mock so it's replicating my application ? For information, here is my App class :

public partial class App : PrismApplication
{
    public App(IPlatformInitializer initializer = null) : base(initializer) { }

    protected override void OnInitialized()
    {
        InitializeComponent();

        NavigationService.NavigateAsync("MenuPrincipalPage/ContenuPage/ClubHousePage");
    }

    protected override void RegisterTypes()
    {
        Container.RegisterTypeForNavigation<MenuPrincipalPage>();
        Container.RegisterTypeForNavigation<ClubHousePage>();
        Container.RegisterTypeForNavigation<AProposPage>();
        Container.RegisterTypeForNavigation<ContenuPage>();
    }
}

MenuPrincipalPage is a MasterDetailPage and ContenuPage is a NavigationPage.

My second question is : how to assert that the navigation stack is now MenuPrincipalPage/ContenuPage/ClubHousePage ?

Many thanks for reading it all and for your answers !!!
Julien

回答1:

What you are proposing is to unit test Prism not your code. Unit testing your ViewModel is a good thing, but you only need to provide a Mock that provides you a way to verify that the navigation string/parameters you expected were valid. For example your Mock may look something like:

public class MockNavigationService : INavigationService
{
    public string LastNavigationString { get; private set; }
    public NavigationParameters LastNavigationParameters { get; private set; }

    public Task NavigateAsync(string name, NavigationParameters parameters, bool? useModalNavigation = null, bool animated = true)
    {
        LastNavigationString = name;
        LastNavigationParameters = parameters;
        return Task.FromResult(0);
    }
}

What it sounds like to me is that what your real goal is though is not to run a unit test but to do a UITest that could validate the navigation and User experience with your application.