Wednesday, August 13, 2008

What's an Auto Mocking Container?

An auto mocking container (AMC) sounds pretty scary, but it's a really neat tool if you're writing a lot of unit tests and find yourself forever constructing mock objects. In the same way that an IoC container knits together dependencies at runtime, an AMC can create all your mock objects automatically for your unit tests.

Say you are testing this Reporter class

public class Reporter
{
  private readonly IReportBuilder reportBuilder;
  private readonly IReportSender reportSender;

  public Reporter(IReportBuilder reportBuilder, IReportSender reportSender)
  {
      this.reportBuilder = reportBuilder;
      this.reportSender = reportSender;
  }

  public void SendReports()
  {
      var reports = reportBuilder.GetReports();

      foreach (var report in reports)
      {
          //reportSender.SendReport(report);
      }
  }
}

Using Rhino.Mocks you might do something like this.

[Test]
public void SendReports_ShouldCreateReportsAndSendThem()
{
  // create the mock services that the Reporter requires
  var reportBuilder = MockRepository.GenerateStub<IReportBuilder>();
  var reportSender = MockRepository.GenerateStub<IReportSender>();

  // create the reporter, injecting the mock services
  var reporter = new Reporter(reportBuilder, reportSender);

  // create some reports
  var report1 = new Report();
  var report2 = new Report();
  var reports = new[] {report1, report2};

  // when the reportBuilder mock's GetReports method is called, return the reports
  // we created above
  reportBuilder.Expect(rb => rb.GetReports()).Return(reports);

  // excercise the method under test
  reporter.SendReports();

  // verify that the reporter sent the expected reports
  reportSender.AssertWasCalled(rs => rs.SendReport(report1));
  reportSender.AssertWasCalled(rs => rs.SendReport(report2));
}

After the tenth time you've had to write this kind of code you get quite bored of typing the mock object creation. It's irritating when you're thinking about using a new dependency in an existing class, and you have to add it to both the class under test and the setup for the test.

Here's an example using the AutoMockingContainer from Rhino Tools.

[Test]
public void SendReports_ShouldCreateReportsAndSendThem_WithAMC()
{
  // create a new auto mocking container
  var mocks = new MockRepository();
  var container = new Rhino.Testing.AutoMocking.AutoMockingContainer(mocks);
  container.Initialize();

  // just ask for the reporter
  // the container will automatically create the correct mocks
  var reporter = container.Create<Reporter>();

  // create some reports
  var report1 = new Report();
  var report2 = new Report();
  var reports = new[] { report1, report2 };

  // when the reportBuilder mock's GetReports method is called, return the reports
  // we created above
  container.Get<IReportBuilder>().Expect(rb => rb.GetReports()).Return(reports);

  // expect that the reporter sent the expected reports
  container.Get<IReportSender>().Expect(rs => rs.SendReport(report1));
  container.Get<IReportSender>().Expect(rs => rs.SendReport(report2));

  // excercise the method under test
  reporter.SendReports();

  // assert that expectation were met
  mocks.VerifyAll();
}

As you can see, after we set up the AMC we simply ask it for an instance of the class we wish to test. We don't have to worry about supplying the dependencies because the AMC works out what mocks need to be created and does it for us.

When we setup our expectations we can ask the AMC for the mock objects it's created.

You can get the latest version of the source code for the Rhino Tools AMC by pointing tortoise here:

https://rhino-tools.svn.sourceforge.net/svnroot/rhino-tools/trunk

Or you can download the code and grab the assemblies I've built from the trunk here

http://static.mikehadlow.com/Mike.AutoMockingContainer.zip

2 comments:

Sohan said...

I was thinking of nearly the same thing conceptually and just posted a blog at http://smsohan.blogspot.com
You may check it!

Mike Hadlow said...

Hi Sohan, From reading your blog I see you're quite a fan of IoC containers. You've used Ninject, Spring.net and Unity. The Rhino Tools AMC is based on the Castle Windsor IoC container. Aren't there AMCs based on Ninject, Spring.net or Unity? It would probably be easier to leverage an existing IoC container for this rather than starting from scratch as you have done in your post.