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
2 comments:
I was thinking of nearly the same thing conceptually and just posted a blog at http://smsohan.blogspot.com
You may check it!
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.
Post a Comment