A small contingent from my work made the trek out to the first Sydney ALT.NET meeting last night. It was great to be in a room full of people all intent on finding better ways to develop software. Afterward I was dragged kicking and screaming (</sarcasm>
:)) by my colleagues to a local pub for debriefing over beers and a laptop. One topic discussed was the Arrange, Act, Assert (AAA) style of mocking using Rhino Mocks 3.5.
I thought I’d quickly run through my (admittedly basic) understanding of AAA, as I tend to use a slightly different approach to the one shown in the meeting. If I’ve got anything hideously wrong please leave a comment and let me know. I’m not going to cover anything about how to use mocking, but will just attempt to outline the difference between record/replay and AAA.
Record / Replay semantics
The traditional way of mocking has been to use record/replay. This means you record a number of expectations against a mock object, then change the mock to replay mode and exercise the subject under test (SUT). In replay mode, the mock will throw an exception if an unexpected method is called (for strict mocks). The final step is to verify the expectations you recorded, which will throw an exception if one of the expected methods was not called.
Let’s have a look at one of my dodgy-as-usual examples (probably even worse than normal, as it was written in a pub around 11 pm after a long day :))
public interface IEmailService { void Send(MailMessage msg); } public class InvoiceSender { private readonly IEmailService emailService; public InvoiceSender(IEmailService emailService) { this.emailService = emailService; } public void SendInvoice(float amount, string to) { var msg = new MailMessage("[email protected]", to, "Invoice", string.Format("Please pay {0}", amount)); emailService.Send(msg); } }
Our subject under test is InvoiceSender
, and we want to verify that the IEmailService.Send(MailMessage)
method is being called from SendInvoice(float, string)
.
[Test] public void Send_invoice_using_email_service_with_record_replay() { var mockRepo = new MockRepository(); var mockEmailer = mockRepo.DynamicMock<IEmailService>(); var invoiceSender = new InvoiceSender(mockEmailer); //Record expecations mockEmailer.Expect(service => service.Send(null)).IgnoreArguments() ; mockRepo.ReplayAll(); //Exercise SUT invoiceSender.SendInvoice(1.0F, "[email protected]"); //Verify expectations mockRepo.VerifyAll(); }
Here we’ve used Rhino Mocks to generate a mock implementation of the IEmailService
interface. We have recorded a single expectation against it: it is expecting to have its Send(...)
method called with any argument (hence the IgnoreArguments()
call – for non-pub code we would probably want to check the argument).
We then use ReplayAll()
to switch to replay mode, which tells our mocks that we have finished recording expectations and are ready to see what is really called on our mock. We then exercise the SUT, and verify that our expectations were met (i.e. Send()
was called on our mock). The test passes – victory is ours!
Writing the test using AAA
There’s nothing really wrong with the record/replay approach. If you like it that’s great! Some people find it confusing (or at least unnatural), probably because it doesn’t quite fit the four phase test structure used for state-based testing (Setup, Exercise, Verify, Teardown).
The AAA approach lets us use a more state-based testing approach with our mocks. Let’s rewrite our previous test using AAA:
[Test] public void Send_invoice_using_email_service_with_AAA() { //Arrange var mockEmail = MockRepository.GenerateMock<IEmailService>(); var invoiceSender = new InvoiceSender(mockEmail); //Act invoiceSender.SendInvoice(1.0F, "[email protected]"); //Assert mockEmail.AssertWasCalled(service => service.Send(Arg<MailMessage>.Is.Anything)); }
Here we are creating our mock using the new static GenerateMock<T>()
method introduced in Rhino Mocks 3.5. We then exercise the SUT with an identical line of code to the one used in the first test. Finally, we assert that the Send()
method on our mock was called as we expected. The test passes – again we are victorious!
First couple of things to notice are that we have no mention of recording or replaying expectations, and we’ve used less lines of code. Our test also fits in with the four phase test structure: Arrange -> Setup, Act -> Exercise, Assert -> Verify, with optional Teardown. This avoids mixing expectations and assertions throughout the test. Depending on your prior experience with record/replay, you might find this easier to read and understand than our first test.
Under the hood Rhino Mocks is still going off and doing pretty much the same thing as its always done. The static GenerateMock<T>()
method simply creates a dynamic mock already in replay mode. The mock remembers all calls against it, and we can then use AssertWasCalled()
and other methods to check these calls and make sure the ones we want are there.
This is only a very simplistic example to illustrate the basic differences for each approach. For more realistic cases the benefits of AAA become more apparent (see Jimmy Bogard’s post on AAA with BDD tests for a good example).
These couple of lines of code are available from my Google Code repository if you want run the tests and have a bit of a play around: DaveSquared.MockSample.zip.