This is actually very simple to do. There are just a couple of things to put in place to cope with the singleton nature of MvvmCross IoC
Also, please note that if you want to test on MonoTouch then because there are dynamic code restrictions, then you cannot use Dynamic Mocks (like Moq)
*1. First in the test setup, I clear the IoC system using code like:
private IMvxServiceProviderRegistry _ioc;
[SetUp]
public void SetUp()
{
// fake set up of the IoC
MvxOpenNetCfContainer.ClearAllSingletons();
MvxOpenNetCfServiceProviderSetup.Initialize();
_ioc = MvxServiceProvider.Instance;
_ioc.RegisterServiceInstance(new MvxDebugTrace());
MvxTrace.Initialize();
}
*2. Then to test general navigation I use a couple of Mocks like:
public class MockMvxViewDispatcherProvider : IMvxViewDispatcherProvider
{
#region IMvxViewDispatcherProvider implementation
public IMvxViewDispatcher Dispatcher { get; set; }
#endregion
}
public class MockMvxViewDispatcher : IMvxViewDispatcher
{
public List CloseRequests = new List();
public List NavigateRequests = new List();
public MockMvxViewDispatcher()
{
}
#region IMvxViewDispatcher implementation
public bool RequestNavigate(MvxShowViewModelRequest request)
{
NavigateRequests.Add(request);
return true;
}
public bool RequestClose(IMvxViewModel whichViewModel)
{
CloseRequests.Add(whichViewModel);
return true;
}
public bool RequestRemoveBackStep()
{
throw new NotImplementedException();
}
#endregion
#region IMvxMainThreadDispatcher implementation
public bool RequestMainThreadAction(Action action)
{
action();
return true;
}
#endregion
}
The main thing these Mocks appear to do is to catch and store the navigation requests.
IMPORTANT NOTE: however, please do note the IMvxMainThreadDispatcher code - this service is essential to much of the operation of MvvmCross - lots of things in MvvmCross rely on this service being available because they need to get their execution across to some "UI thread" - e.g. PropertyChanged will silently fail to fire if the service is missing!
*3. To then test navigation I use tests like:
[Test]
public void ExecutingTheLoginCommandNavigatesToTheLoginViewModel()
{
var mockNavigation = new MockMvxViewDispatcher();
var mockNavigationProvider = new MockMvxViewDispatcherProvider();
mockNavigationProvider.Dispatcher = mockNavigation;
_ioc.RegisterServiceInstance(mockNavigationProvider);
var homeViewModel = new HomeViewModel();
homeViewModel.LoginCommand.Execute(null);
Assert.That(mockNavigation.NavigateRequests.Count == 1);
Assert.That(mockNavigation.NavigateRequests.First().ViewModelType == typeof(LoginViewModel));
}
[Test]
public void ExecutingTheCloseCommandClosesTheViewModel()
{
var mockNavigation = new MockMvxViewDispatcher();
var mockNavigationProvider = new MockMvxViewDispatcherProvider();
mockNavigationProvider.Dispatcher = mockNavigation;
_ioc.RegisterServiceInstance(mockNavigationProvider);
var loginViewModel = new LoginViewModel();
loginViewModel.CloseCommand.Execute(null);
Assert.That(mockNavigation.CloseRequests.Count == 1);
Assert.That(mockNavigation.CloseRequests.First() == viewModel);
}
*4. And to test ViewModel property changes I just use simple code like:
[Test]
public void ExecutingTheAddCommandIncrementsTheCounter()
{
var mockNavigation = new MockMvxViewDispatcher();
var mockNavigationProvider = new MockMvxViewDispatcherProvider();
mockNavigationProvider.Dispatcher = mockNavigation;
_ioc.RegisterServiceInstance(mockNavigationProvider);
var tipViewModel = new TipViewModel();
Assert.That(tipViewModel.Counter == 0);
var notifications = new List();
tipViewModel.PropertyChanged += (s, e) =>
{
notifications.Add(e.PropertyName);
};
tipViewModel.CloseCommand.Execute(null);
Assert.That(mockNavigation.CloseRequests.Count == 0);
Assert.That(mockNavigation.NavigateRequests.Count == 0);
Assert.That(notifications.Count == 1);
Assert.That(notifications[0] == "Counter");
Assert.That(tipViewModel.Counter == 1);
}
*5. And that's it... these tests work cross-platform - including in the lovely NUnit tool from Xamarin for MonoTouch
Full image: http://i.imgur.com/XJHpD.png
Obviously there is still a need for:
- event handling - using ICommand or Delegates somehow
- some elements need tweaking - especially custom Views and Images
- integration with binding (for MvvmCross)
However, I think the basic idea should work pretty well across Android.Dialog and also the mvvmcross fork of MonoTouch.Dialog.And I do intend to add a WinRT.Dialog and a WindowsPhone.Dialog too...
If anyone's interested, then post here or on https://github.com/slodge/Android.Dialog/issues/4