Arrange Act Assert and BDD specifications


With Rhino Mocks 3.5 just around the corner, I’ve started using it to create much more readable tests.  One of the things that always bothered me with Expect.Call, constraints and the like was that it mixed in the Arrange with Assert.  For those that haven’t heard of AAA, it’s a pattern for authoring unit tests:

  • Arrange – set up the unit under test
  • Act – exercise the unit under test, capturing any resulting state
  • Assert – verify the behavior through assertions

As I moved towards BDD context/specification style tests, working with Rhino Mocks didn’t fit the picture very well.  But with the new AAA syntax of Rhino Mocks 3.5, I can very cleanly separate out the behavior I want to observe from the mechanics of setting up the test.

Here’s a normal unit test, as I would have written it about a year ago:

[Fact]
public void Should_send_email_when_order_is_over_200()
{
    MockRepository repo = new MockRepository();

    //Arrange
    ISmtpClient mockClient = repo.DynamicMock<ISmtpClient>();
    IOrderSpec stubSpec = repo.DynamicMock<IOrderSpec>();
    MailMessage actual = null;

    Order order = new Order();
    order.Total = 201.0m;

    using (repo.Record())
    {
        mockClient.Send(null);

        // Also assert?
        LastCall
            .IgnoreArguments()
            .Do(new Action<MailMessage>(message => actual = message));

        SetupResult
            .For(stubSpec.IsMatch(order))
            .Return(true);
    }

    OrderProcessor pr = new OrderProcessor(mockClient, stubSpec);

    // Act
    pr.PlaceOrder(order);

    // Assert
    actual.ShouldNotBeNull();
    actual.To.Count.ShouldEqual(1);
    actual.To[0].Address.ShouldEqual("salesdude@email.com");

    repo.VerifyAll();
}

It’s a really long test, but the basic idea is that an email needs to be sent out to the sales guy when big orders get placed.  The sales guy wanted to follow up immediately, to try and sell more (we think).  I see many issues with this test:

  • It’s frickin’ huge
  • I can’t tell what the point of it is at first glance
  • It’s really hard to tell what’s being tested

Moving towards the context/specification style, but still with Rhino Mocks improved things somewhat, but it’s still quite awkward:

public class When_placing_a_large_order 
    : ContextSpecification
{
    private MockRepository _repo;
    private OrderProcessor _orderProcessor;
    private Order _order;
    private MailMessage _actual;

    protected override void EstablishContext()
    {
        _repo = new MockRepository();

        ISmtpClient mockClient = _repo.DynamicMock<ISmtpClient>();
        IOrderSpec stubSpec = _repo.DynamicMock<IOrderSpec>();
        _actual = null;

        _order = new Order();
        _order.Total = 201.0m;

        using (_repo.Record())
        {
            mockClient.Send(null);

            // Also assert?
            LastCall
                .IgnoreArguments()
                .Do(new Action<MailMessage>(message => _actual = message));

            SetupResult
                .For(stubSpec.IsMatch(_order))
                .Return(true);
        }

        _orderProcessor = new OrderProcessor(mockClient, stubSpec);
    }

    protected override void Because()
    {
        _orderProcessor.PlaceOrder(_order);
    }

    [Test]
    public void Should_send_the_email_out()
    {
        _actual.ShouldNotBeNull();
    }

    [Test]
    public void Email_sent_should_be_addressed_to_the_sales_guy()
    {
        _actual.To.Count.ShouldEqual(1);
        _actual.To[0].Address.ShouldEqual("salesdude@email.com");
    }

    [Test]
    public void Should_verify_all_expectations()
    {
        _repo.VerifyAll();
    }
}

Again, I have to do some strange things to capture the output, and the record/replay model doesn’t jive well with BDD-style specifications.  I always had this one observation that said, “Should verify all expectations”.  Not very interesting, and not descriptive of the behavior I want to observe.  It doesn’t describe any behavior, just some cleanup assertion for the MockRepository.

Finally, let’s see how the AAA syntax of Rhino Mocks 3.5 clears things up:

public class When_placing_a_large_order 
    : ContextSpecification
{
    private OrderProcessor _orderProcessor;
    private Order _order;
    private ISmtpClient _mockClient;

    protected override void EstablishContext()
    {
        _mockClient = Dependency<ISmtpClient>();
        IOrderSpec stubSpec = Stub<IOrderSpec>();

        _order = new Order();
        _order.Total = 201.0m;

        stubSpec.Stub(x => x.IsMatch(_order)).Return(true);

        _orderProcessor = new OrderProcessor(_mockClient, stubSpec);
    }

    protected override void Because()
    {
        _orderProcessor.PlaceOrder(_order);
    }

    [Test]
    public void Should_send_the_email_to_the_sales_guy()
    {
        _mockClient.Expect(x => x.Send(Arg<MailMessage>.Matches(msg => msg.To[0].Address == "salesdude@email.com")));
    }
}

That’s a lot smaller!  I clearly separate the Arrange (EstablishContext) from Act (Because) and the Assert, which is my actual observation.  To stub the indirect input of the IOrderSpec, I can use the Stub extension method provided by Rhino Mocks 3.5.

But the best aspect of the new AAA syntax is that I can finally create readable specifications that use Rhino Mocks.  Before, all of the noise of the Record/Replay and the MockRepository obscured the intention of the specification.  I had to rely on test spies earlier to capture the output of the ISmtpClient.Send call, as the old constraint model would have mixed in the Assert with the Arrange (i.e., I would have to put the constraints in the record section.  Not pretty.)

I’ve found that without the distractions of the old Rhino Mocks syntax, I can better focus on the behavior I’m trying to observe.  It’s now just one line to set up indirect inputs with stubs, and one line to verify interactions and indirect outputs.

Showing some MVC support