Software Design Blog

Journey of Rinat Abdullin

Testing MVC Controllers With Mock Container

While developing components and services for the IoC environment, we end up with highly-specialized classes (which is good) that tend to consume a number of other lower-level classes, abstracted behind interfaces.

While this increases flexibility and stability of the system, testing could become slightly more complicated.

Let us consider following .NET controller class (of the Model-View-Controller pattern):

[UsedImplicitly]
[Guidelines.Controller]
public sealed class ExcelTaskEditController
{
  public ExcelTaskEditController(IExcelTaskEditView view,
                                 IExcelWorkbook workbook)
  {
    Enforce.Arguments(() => view, () => workbook);

    _view = view;
    _workbook = workbook;
  }

  public void Startup()
  {
    _view.EditRange += (range, message, action) =>
      _workbook.GetRange(range, message).Apply(action);
  }

  public Result<ExcelTask> Edit(Maybe<ExcelTask> request)
  {
    // Logic of binding Model to View is skipped here
    // for brevity
    return _view.GetModel(ExcelTaskIs.Valid);
  }
}

If you’re interested in the logic behind view.GetModel - check out the article on DDD and Rule-driven UI Validation in MVC.

Participation of this controller in the system composition is limited to a single line:

builder.RegisterEditor<ExcelTaskEditController, ExcelTask>(
  c => c.Startup(), c => c.Edit);

In this line we simply tell the Autofac: ”When some view requests a capability to present user with UI to edit/create ExcelTask, pass it an edit delegate of the pre-configured controller”. This does resemble MEF slightly.

That’s nice so far, but how do we unit test the actual controller? Here are some options:

  • manually create mock classes for the View and Workbook interfaces it consumes and pass them to the constructor;
  • be smart and use some mocking library to create each of these mocks/stubs for us.

However, there is an easier approach:

With this approach unit test to check editing of a task could look like this:

[Test]
public void Editing_task()
{
  // action to test
  var result = Controller.Edit(Sample.ValidTask);
  // when task is edited, we should
  // bind it to a view
  // hide "Delete" button
  _container
    .AssertWasCalled<IExcelTaskEditView>(v => v.BindModel(Sample.ValidTask))
    .AssertWasCalled<IExcelTaskEditView>(v => v.SetDeleteVisible(true));
  // expected result
  Assert.AreEqual("my result", result.ErrorMessage);
}

And the setup code for the NUnit test fixture looks like this in C#:

[TestFixture]
public sealed class ExcelTaskEditControllerTests
{
  MockContainer<ExcelTaskEditController> _container;

  ExcelTaskEditController Controller
  {
    get { return _container.Subject; }
  }

  [SetUp]
  public void Setup()
  {
    // create new container, subject is registered automatically
    _container = MockContainer.For<ExcelTaskEditController>();
    // initialize the controller
    Controller.Startup();
    // tell mocking library:
    // when there is a specific call to a view interface, 
    // return predefined result
    _container
      .Resolve<IExcelTaskEditView>()
      .Stub(v => v.GetModel(ExcelTaskIs.Valid))
      .Return(Result<ExcelTask>.Error("my result"));
  }

Implementation of MockContainer class is rather straightforward in .NET. All it does is:

  • whenever an unknown service/component is requested - create a new stub for it with Rhino.Mocks library and register in the container as Singleton;
  • provide a few shortcuts for frequently used mocking/assertion methods.

MockContainer class could be found in Lokad.Testing.dll assembly that has been introduced to the Lokad Shared Libraries. This new assembly includes:

  • Rhino.Mocks.dll merged in for mocking purposes;
  • Mocking Container implementation on top of Autofac IoC and Rhino Mocks.

Lokad.Testing.dll depends on Lokad.Shared.dll and Lokad.Stack.dll.

You can check out the project page for more details or simply download the latest binaries.

MVC Controllers from this example are a specific case of using Component-Driven Development approach, that helps to build reliable software efficiently.

There is also another article on this topic: Testing MVC Elements and Interactions with Mock Container that you might like.