Skip to content

Instantly share code, notes, and snippets.

@marukami
Last active September 8, 2015 04:29
Show Gist options
  • Save marukami/f2b8ca3a1f594ff7c7a9 to your computer and use it in GitHub Desktop.
Save marukami/f2b8ca3a1f594ff7c7a9 to your computer and use it in GitHub Desktop.
Unit Testing MvvmCross

Just some notes on Unit Testing MvvmCross.

#Setup Test Project

  1. Create an empty project
  2. NOTE: depending on how you setup your Xamarin project you might need multiple projects for iOS, Android and PCL
  3. Add the follow dependencies
  4. AutoFixture - Test Data Genertator
  5. Moq - Object Mocking framework
  6. MvvmCross
  7. NUnit
  8. Any other MvvmCross modules as needed
  9. Refence the

Setting up a test class

Now we should have all our tools we need for doing unit testing. So, lets create a test class

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Cirrious.CrossCore.Core;
using Cirrious.MvvmCross.Plugins.Messenger;
using Cirrious.MvvmCross.Test.Core;
using Cirrious.MvvmCross.Views;
using Moq;
using NUnit.Framework;
using Ploeh.AutoFixture;
using Cirrious.MvvmCross.ViewModels;
using Cirrious.MvvmCross.Platform;
// Add any more project specific namespaces

namespace ProjectX.PCL.Tests
{
    [TestFixture]
    // You need to subclass MvxIoCSupportingTest
    public class CustomerViewModelTests : MvxIoCSupportingTest {
	
		// CustomerViewModel all dependentcies
		Mock<ICustomerService> _customerService;
		Mock<IStateFullyDependency> _stateFullyDependency;
		
		// Test Data generator
		readonly Fixture _fixture;
		public CustomerViewModelTests() {
		  _fixture = new Fixture ();
		}
		
		// VM generator. This can help if you have a lot of dependentcies
		CustomerViewModel GetModel =>  new CustomerViewModel (customerService.Object);
		
		// Setup is called before each test is run
		[SetUp]
		public void init() {
			base.ClearAll (); // This resets the IoC container.

			// Setup 
			_customerService = new Mock<ICustomerService> ();
			Ioc.RegisterSingleton<ICustomerService> (_customerService.Object);
		}
		
		// If you need to clean up anything after each test use the 
		// TearDown attribute
		[TearDown] 
		public void Cleanup() {
			// Code Dragons be here. 
		}
		
		// add Tests
		[Test]
		public void TestNameHere() {}
	}
}

Testing ShowViewModel

I'm sure everyone has had it where they passed the wrong paramaters to a show viewModel I know I've had this happen a few tiems.

To make sure your VM is passing the correct keys to a ShowViewModel you will need

Testing ShowViewModel

If we want to test our ViewModel is passing the expected keys to ShowViewModel we need to hook into the ShowViewModel method handler

First we need to subclass the MvxMainThreadDispatcher. In this case I'm only checking that all the keys I expect are present and are been passed to the ShowViewModel form the ViewModel

class NavMockDispatcher : MvxMainThreadDispatcher, IMvxViewDispatcher {
	readonly List<string> _expectedKeys;
	
	public NavMockDispatcher (List<string> expectedKeys) {
		this._expectedKeys = expectedKeys;
	}
	
	public override bool ShowViewModel (MvxViewModelRequest request) {
		var result = false;
		foreach (var key in request.ParameterValues.Keys) {
			result = _expectedKeys.Contains (key);
			// Soon as we find a missing key exit the loop
			if (!result) {
				break;
			}
		}
		// If we found all keys result should be true, 
		// If any keys where missing result should be false
		Assert.IsTrue (result);
		return result;
	}
	
	// We don't need these methods for the test. Here are the stubs in anycase
	public virtual bool ChangePresentation (MvxPresentationHint hint)
	{
		throw new NotImplementedException ();
	}

	public virtual bool RequestMainThreadAction (Action action)
	{
		action ();
		return true;
	}
}

Now we have our MvxMainThreadDispatcher setup how do we test the CustomerViewModel ShowViewModel. In this example Lets assume we have a show banking view model method.

// .... CustomerViewModel
public bool ShowBankingView() {
	ShowViewModel(new { customerId = _id, authToken = _bankToken } );
}
// .... CustomerViewModel

Then our test would look somthing like.

[Test]
public void ShowBankingView_Should_Pass_ShowViewModel_ExpectedKeys() {
	setupDefaultServiceData ();

	var mockDispatcher = new NavMockDispatcher (new List<string>{
		"customerId", "authToken"
	});
	// Just for completness lets register mockDispatcher with both the IMvxViewDispatcher
	// and MvxMainThreadDispatcher with the IoC
	Ioc.RegisterSingleton<IMvxViewDispatcher> (mockDispatcher);
	Ioc.RegisterSingleton<MvxMainThreadDispatcher> (mockDispatcher);
	// You also need to register MvxStringToTypeParser.
	// Not sure if this is needed when using a Dict<string,string>
	// Since I'm using an anonymous classes it appears to be required
	Ioc.RegisterSingleton<IMvxStringToTypeParser> (new MvxStringToTypeParser());

	// Get a new model by calling our factory get property
	var customerModel = GetModel; 
	customerModel.ShowBankingView ();
	
	// Remeber the Assert is done inside the NavMock
}

Testing Raise Property Changed

If you have any Property Changed events that alter other backing stores apart from their own you may find your testing failing. Don't worry this is expected behaviour. To test property changed propagations you need to subclass the MvxMainThreadDispatcher. Like we did with the NavMock before we need to subclass the MvxMainThreadDispatcher.

In this case I'm going to make another subclass on the MvxMainThreadDispatcher. That's it you're test should now pass.

class NavMockDispatcher : MvxMainThreadDispatcher, IMvxViewDispatcher {
	
	public override bool ShowViewModel (MvxViewModelRequest request) {
		throw new NotImplementedException ();
	}
	
	public virtual bool ChangePresentation (MvxPresentationHint hint)
	{
		throw new NotImplementedException ();
	}

	public virtual bool RequestMainThreadAction (Action action)
	{
		action ();
		return true;
	}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment