Follow Rinat Abdullin
« Deployment and Updates of Desktop Applications: WiX, ClickOnce and Other Scenarios | Main | Cloud Bursting Scenarios For Small Companies »
Thursday
25Jun2009

Testing MVC Elements and Interactions with Mock Container

Lokad Shared Libraries project includes implementation of the MockContainer that significantly simplifies testing of Model-View-Controller elements in separation. It does this by automatically generating and injecting mock objects, should the tested class ask for them in its constructor.

These mock objects are persisted in the local container instance as singleton, so that additional expectations could be recorded against them. Expectations are verified when the container is disposed.

There is a little overhead of creating and disposing container and instances every test, since we are using Autofac IoC Container internally here, which has a really good performance.

Interface and internals of this MockContainer have been polished heavily since its introduction a few months ago. This (with the help from a base fixture class providing a nice syntax) has allowed to write tests like this one:

[Test]
public void Display()
{
  View.SetTitle("Some title");

  // we expect the view to ask IViewspace to display itself
  WhenViewIsShown
     // you always set Form.CancelButton  for user's pleasure
    .CancelButtonShouldBeSet()
     // assert that title as as expected
    .TitleShouldBe("Some title");

    // create 0 to 20 random models and display them in view
    View.BindModel(RandClientModels.NextEvents(0, 20));
}

This is an actual unit test against actual Windows.Forms.UserControl component. It does what it says. Ability to write unit tests like this brings following benefits for free:

  • In this unit test all calls are executed against the interface. So should we need add Silverlight or WPF view implementations to our library, we would already have tests for the first step of Test Driven Development.
  • Ability to write simple unit tests really pays off in scenarios, where a single component is reused in multiple projects. Finding a bug in one of these projects, expressing it in a unit test and fixing, immediately makes all projects downstream more stable.
  • Using randomly-generated Models allows to cover a wider range of the possible failure scenarios. Should some case go wrong, we'll be able to reproduce it by checking out rand seed used in the integration run (Nondeterministic reproducible testing will be the subject of another article).

Here's another type of unit test. It could be invoked explicitly just to see how our view looks alone (without launching an entire application and clicking our way to this View):

[Test, Explicit]
public void Show_view_for_debugging()
{
  // when view asks to show itself, actually display it
  WhenViewIsShown.ShowAsDialog();
  // load random values and display
  View.BindModel(RandClientModels.NextEvent());
  // display view, while using ValidEvent rule 
  // to validate all input
  View.GetModel(EditEventController.ValidEvent);
}

ExplicitAttribute in NUnit tells us that unit test is excluded from the usual unit testing run and has to be launched manually.

This kind of debugging unit test for the reusable View components speeds up all the testing and UI tweaking process. Do you need to check out how View validates data, scales or displays that recent UI tweak? This is just one click away:

Using MockContainer to test MVC Views

Sometimes we may want to go deeper and check out how various components integrate together. This is not a normal unit test (in the sense of Unit Testing), so it is marked with ExplicitAttribute as well.

[Test, Explicit]
public void Test_for_debugging_with_controller()
{
  // view is registered by test fixture
  WhenViewIsShown.ShowAsDialog();

  // simplified syntax for registering additional components
  // that we don't want to be replaced with mock objects.
  Container.Build(builder =>
  {
    builder.Register<ConnectionEditController>();
    builder.Register<WorkScopeView>().As<IWorkScopeView>();
  });

  // resolving controller and launching it with a random connection
  var controller = Container.Resolve<ConnectionEditController>();
  controller.Startup();
  controller.Edit(RandApiModels.NextServiceConnection());
}

Although code like this does not participate in the Continuous Integration process, having it around speeds up the development and simplifies debugging, should something go wrong.

This specific test creates UI elements like this:

Using MockContainer to test complex component interactions

Unit tests samples come from open source project Lokad SDK that allows developers to introduce forecasting capabilities of Lokad.com into their applications.

Reader Comments (1)

I really like your ideas for quick testing UI components. It seems way smarter than relaunching the entire app and then navigate to the corresponding part. I will recycle this idea for my software engineering course next year :-)

June 25, 2009 | Unregistered CommenterJoannes Vermorel
Comments for this entry have been disabled. Additional comments may not be added to this entry at this time.