Ok you could just run the entire app through the IoC container and bootstrap the whole thing, this way you get actual concrete implementations for all your interfaces. The downside to that is that you will need everything available (databases, web services etc) and can be more difficult to test an application if you pretty much need it all running (and it may be much slower to execute tests since actual work is being done). If you can easily go this route, then by all means use it.
Alternatively you could mock the behaviour/state of the objects using mocks or stubs by using a mocking framework such as Moq
With Moq you setup your test environment so that your interfaces and classes are represented by Mock<T>
(mock objects) which you specify the behaviour for. Then you test against that behaviour in your ViewModels
Here's an example set of tests using Moq and NUnit for your MainViewModel
in it's current incarnation:
// Decorate with test fixture attribute so NUnit knows it's a test
[TestFixture]
class MainViewModelTests
{
// The interfaces/instances you will need to test with - this is your test subject
MainViewModel _mainVM;
// You can mock the other interfaces:
Mock<IWindowManager> _windowManager;
Mock<IEventAggregator> _eventAggregator;
// Setup method will run at the start of each test
[SetUp]
public void Setup()
{
// Mock the window manager
_windowManager = new Mock<IWindowManager>();
// Mock the event aggregator
_windowManager = new Mock<IEventAggregator>();
// Create the main VM injecting the mocked interfaces
// Mocking interfaces is always good as there is a lot of freedom
// Use mock.Object to get hold of the object, the mock is just a proxy that decorates the original object
_mainVM = new MainViewModel(_windowManager.Object, _eventAggregator.Object);
}
// Create a test to make sure the VM subscribes to the aggregator (a GOOD test, I forget to do this a LOT and this test gives me a slap in the face)
[Test]
public void Test_SubscribedToEventAggregator()
{
// Test to make sure subscribe was called on the event aggregator at least once
_eventAggregator.Verify(x => x.Subscribe(_mainVM));
}
// Check that window state toggles ok when it's called
[Test]
public void Test_WindowStateTogglesCorrectly()
{
// Run the aggregator test at the start of each test (this will run as a 'child' test)
Test_SubscribedToEventAggregator();
// Check the default state of the window is Normal
Assert.True(_mainVM.WindowState == WindowState.Normal);
// Toggle it
_mainVM.ToggleWindowState();
// Check it's maximised
Assert.True(_mainVM.WindowState == WindowState.Maximised);
// Check toggle again for normal
_mainVM.ToggleWindowState();
Assert.True(_mainVM.WindowState == WindowState.Normal);
}
// Test the title changes correctly when the method is called
[Test]
public void Test_WindowTitleChanges()
{
Test_SubscribedToEventAggregator();
_mainVM.ChangeTitle("test title");
Assert.True(_mainVM.Title == "test title");
}
}
You can see how you can test state and behaviour, I expected a certain VM state when VM methods such as ChangeTitle
were called, and I also expected a behaviour (I expected Subscribe(X)
to be called on the aggregator at least once at the start of each test).
The method decorated with [SetUp]
will be called at the start of each test. There are teardown and other methods (including one to setup the entire test fixture, i.e. it only runs once per fixture)
The key thing here is probably that for CM you don't actually need to mock any behaviour in the event aggregator since CM expects you to implement IHandle<T>
for your event aggregator messages. Making these subscriber methods an interface implementation means that you will already have public methods on your object which you can call to simulate event aggregator calls.
So for instance you can use
public class MainViewModel : IHandle<someEventMessageArgument> // (this is your subscriber interface)
{
// (and this is the corresponding method)
public void Handle(someEventMessageArgument message)
{
// do something useful maybe change object state or call some methods
}
}
// Test the method - you don't need to mock any event aggregator behaviour since you have tested that the VM was subscribed to the aggregator. (OK CM might be broken but that's Robs problem :))
[Test]
Test_SomeEventDoesWhatYouAreExpecting()
{
_mainVM.Handle(someEventMessageArgument);
// Assert that what was supposed to happen happened...
Assert.True(SomethingHappened);
}
Check out the Moq quickstart here:
http://code.google.com/p/moq/wiki/QuickStart