Los Techies : Blogs about software and anything tech!

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.

Kick It on DotNetKicks.com
Posted Jul 24 2008, 08:18 AM by bogardj

Comments

jcteague wrote re: Arrange Act Assert and BDD specifications
on 07-24-2008 9:15 AM

What is in your ContextSpecification base class?

Colin Jack wrote re: Arrange Act Assert and BDD specifications
on 07-24-2008 10:02 AM

@jcteague

My guess is very little other than a few virtual/abstract methods for subclasses to override e.g. EstablishContext and Because. Thats what mine is like anyway.

@jimmy

Nice stuff, I do like it but I still find mocking and BDD pretty hairy especially with MVC where you can have quite a lot of mocking/stubbing to do as you end up with quite a lot of detail specified upfront when establishing the context. Better than before though!

On a seperate note do you follow the one-mock approach, so lets say there was also an expectatin that at some point on IOrderSpec...would you do that completely seperately in a different test class?

Rob wrote re: Arrange Act Assert and BDD specifications
on 07-24-2008 11:01 AM

Thanks for the example!  It's nice to see the progression and that clarified some of the confusion I had around the new syntax.

DotNetKicks.com wrote Arrange Act Assert and BDD specifications
on 07-24-2008 12:11 PM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Joe Ocampo wrote re: Arrange Act Assert and BDD specifications
on 07-24-2008 2:58 PM

Is ContextSpecification your own base class? Or is it part of ....?

Victor Kornov wrote re: Arrange Act Assert and BDD specifications
on 07-24-2008 3:45 PM

@Joe

I believe ContextSpecification  comes from NBehave. Also, look for it here blog.jpboodhoo.com/BDDAAAStyleTestingAndRhinoMocks.aspx

bogardj wrote re: Arrange Act Assert and BDD specifications
on 07-24-2008 6:23 PM

@Joe, Victor and John

The ContextSpecification comes from a version of NBehave I still haven't committed to the trunk.  I'll push it out soon after a few more tweaks.

Reflective Perspective - Chris Alcock » 2008 » July » 25 wrote Reflective Perspective - Chris Alcock &raquo; 2008 &raquo; July &raquo; 25
on 07-25-2008 3:10 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; 2008 &raquo; July &raquo; 25

Morten Lyhr wrote re: Arrange Act Assert and BDD specifications
on 07-26-2008 4:41 AM

I wrote about Rhino Mocks 3.5 AAA and BDD here:

morten.lyhr.dk/.../doing-bdd-when-expecting-exception.html

Morten Lyhr wrote re: Arrange Act Assert and BDD specifications
on 07-26-2008 4:42 AM

Whoops... wrong url :-)

I wrote about it here:

morten.lyhr.dk/.../doing-bdd-with-rhino-mocks-aaa-syntax.html

Andrei Butnaru's blog wrote Programming links, 07.26.2008
on 07-26-2008 3:07 PM

Programming links, 07.26.2008

Link-Listing – July 08 « Cav’s Weblog wrote Link-Listing &ndash; July 08 &laquo; Cav&#8217;s Weblog
on 07-28-2008 5:54 PM

Pingback from  Link-Listing &ndash; July 08 &laquo; Cav&#8217;s Weblog

Simone Busoli wrote re: Arrange Act Assert and BDD specifications
on 08-03-2008 10:42 AM

I think the _client.Expect call should be replaced with _client.AssertWasCalled. Are you sure it's really verifying it's been called?

Christopher Bennage wrote Building a WPF Application: Part 2
on 08-16-2008 8:34 PM

Part 0 Part 1 So let&#39;s get back to this whole building a WPF application thing. A number of things

Community Blogs wrote Building a WPF Application: Part 2
on 08-16-2008 9:37 PM

Part 0 Part 1 So let&#39;s get back to this whole building a WPF application thing. A number of things

Playing with BDD: ContextSpecification wrote Playing with BDD: ContextSpecification
on 08-26-2008 7:34 PM

Pingback from  Playing with BDD: ContextSpecification

Ian Cooper [MVP] wrote Learning and crafstmanship
on 09-23-2008 3:17 AM

Roy has a pretty thoughful post on the barrier to entry for most developers with Test-Driven Development

Community Blogs wrote Learning and craftsmanship
on 09-23-2008 3:45 AM

Roy has a pretty thoughful post on the barrier to entry for most developers with Test-Driven Development

Mirrored Blogs wrote Learning and craftsmanship
on 09-23-2008 4:00 AM

Roy has a pretty thoughful post on the barrier to entry for most developers with Test-Driven Development

joey wrote re: Arrange Act Assert and BDD specifications
on 09-23-2008 6:22 AM

Hrmm I agree with Simone, the last code block with AAA syntax looks off according to the documentation at least

Since you are creating a "mock" object for the SmtpClient, you need to verify the expectations that you set on it.

So the arrangement would be:

[code]

<pre>

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);

      //expectation up here

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

       _orderProcessor = new OrderProcessor(_mockClient, stubSpec);

   }

   protected override void Because()

   {

       _orderProcessor.PlaceOrder(_order);

   }

   [Test]

   public void Should_send_the_email_to_the_sales_guy()

   {

       _mockClient.VerifyAllExpectations();

   }

[/code]

</pre>

If you wanted the AAA they would both have to be "Stubs" and in the Assert block you can have:

<pre>

[Test]

   public void Should_send_the_email_to_the_sales_guy()

   {

      _stubClient.AssertWasCalled(x=>x.Send( ...));

   }

</pre>

This is coming from reading this post:

ayende.com/.../Rhino-Mocks--Arrange-Act-Assert-Syntax.aspx

and documentation here:

ayende.com/.../Rhino+Mocks+3.5.ashx

bogardj wrote re: Arrange Act Assert and BDD specifications
on 09-23-2008 7:45 AM

@joey

Yep, you're right.  We've been doing the latter example in practice.  I like asserting individual method calls rather than a bunch of expectations.

Implementing a Customer Search Service – Part 2 « Cav’s Weblog wrote Implementing a Customer Search Service &ndash; Part 2 &laquo; Cav&#8217;s Weblog
on 10-12-2008 1:25 PM

Pingback from  Implementing a Customer Search Service &ndash; Part 2 &laquo; Cav&#8217;s Weblog

TDD: Design tests for failure at Mark Needham wrote TDD: Design tests for failure at Mark Needham
on 01-27-2009 8:49 AM

Pingback from  TDD: Design tests for failure at Mark Needham

Pro Information Center » Blog Archive » Mark Needham: TDD: Design tests for failure wrote Pro Information Center &raquo; Blog Archive &raquo; Mark Needham: TDD: Design tests for failure
on 01-28-2009 5:34 AM

Pingback from  Pro Information Center  » Blog Archive   » Mark Needham: TDD: Design tests for failure

TDD: Test DRYness at Mark Needham wrote TDD: Test DRYness at Mark Needham
on 01-29-2009 7:20 PM

Pingback from  TDD: Test DRYness at Mark Needham

?????????????????????? Behavior Driven Development ???? .NET « butaji wrote ?????????????????????? Behavior Driven Development ???? .NET &laquo; butaji
on 02-03-2010 6:13 PM

Pingback from  ?????????????????????? Behavior Driven Development ???? .NET « butaji

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems