The Transition from TDD to BDD

butterfly I've been a long practitioner of test-driven development (TDD). I first started unit testing back in 2004, and it made a profound effect on me.  It was incredibly difficult, however and I began to look for ways to make my code more testable. This brought me to TDD, which guided me towards looser-coupled systems that were testable from the get-go (since we started out testing it!)

Things in the TDD world aren't all peach schnapps and roses (or however the saying goes). Often times you still end up with messy tests that have tons of code duplication and very little solubility. TDD is still difficult for me, even though I've done it for 4 years.

One thing I've noticed lately that I've been slowly losing in my tests is the executable documentation. It's increasingly hard to tell how my system behaves just from reading my unit tests. As our system grew more complex, so did our unit tests and eventually they lost most of their grokability.

Another thing I've noticed is that our tests have so much setup gunk that it clouds the meaning of each test method. By nature each test likely has some particular context in which it intends to run, and this code gets placed in the [Test] itself rather than the [SetUp]. Why?  My mocks need to behave differently in each test method, so if I put them in the setup, things either don't work for other tests or there are mocks being set up that are never used for other tests. I directly attribute this to having one [TestFixture] per system under test.

I'd like to state that again, because it bears repeating. One TestFixture per system under test is an anti-pattern. It has taken me three (3!) presentations on Behavior-Driven Design for this fact to really sink in.  Contexts are important, and a system that you want to test certainly has more than one context. By separating the fixtures into per-system-context rather than per-system, we can now leverage our [Setup] again to establish our context!

Here is a test class that I might have written with a strict TDD mindset:

//PostControllerTester.cs
[Test]
public void edit_action_gets_post_from_repository()
{
    _authService.Stub(x=>x.IsAuthorizedToEdit("bob"))
        .IgnoreArguments()
        .Return(true);

    var controller = CreatePostController();
    controller.Edit(5);
    _repository.AssertWasCalled(x=>x.Find(5));
}

Notice that this test actually has some setup, some sort of action (in this case we're invoking the Edit action), and some verification.  This test has 3 pieces to it!  Applying a more BDD mindset, you'd likely have something like this:

public class when_invoking_edit_action_as_authorized_user()
{
    ...
    [SetUp]
    public void Setup
    {
        _controller = new PostsController(...);
        _authService = MockRepository.GenerateMock<IAuthService>();
        _repository = MockRepository.GenerateMock<IPostRepository>();
        EstablishContext();
        When();
    }
    public void EstablishContext()
    {
        _authService.Stub(x=>x.IsAuthorizedToEdit("bob"))
            .IgnoreArguments().Return(true);
    }
    public void When()
    {
        _controller.Edit(5);   
    }
    [Test]
    public void it_should_fetch_post_from_repository()
    {
        _repository.AssertWasCalled(x=>x.Find(5));
    }
}

Notice the name of the class.  It describes the context of what we're trying to accomplish.  Our [SetUp] now establishes the state of the environment in which we want to act on our system.  The When() method is where we act on our system.  The only thing remaining is the test which is now dead simple.  The test simply verifies some aspect of the system.  The name of the test is also only conveying the verification part of the test.

We can now easily glance at this test (or spec, which it is sometimes called) and read it like english:

When invoking the edit action as an authorized user, it should should fetch the post from the repository.

This looks awfully close to acceptance criteria that we write for our user stories. 

Any acceptance tests that share the same context can reside in the same fixture.  It's perfectly normal to have 1 test per fixture, but at times you'll find that you can have 2 or 3 that indeed share the same context, and those can reside in the same fixture class.

 

BDD Frameworks

There are plenty of BDD frameworks out there that will do their best to impose their view of BDD on you, and this can seriously hinder your ability to naturally understand the why/how of BDD.  Later on, once you have your own opinions, you can leverage a framework (such as MSpec) to have a more terse syntax for describing behaviors in your system.

BDD Base Class

Until you get BDD, stick to what you know.  In my example I call EstablishContext() and When(), and I'll do that for every fixture.  Why not leverage a base class?  Here's mine:

public abstract class Specification
{
    protected Exception ExceptionThrown { get; private set; }

   
[SetUp]
    public void Setup()
    {
        EstablishContext();

        try
        {
            When();
        }
        catch(Exception exc)
        {
            ExceptionThrown = exc;
        }
    }

   

protected T Mock<T>()
    {
        return MockRepository.GenerateMock<T>();
    }

    protected abstract void EstablishContext();
    protected abstract void When();

   

[TearDown]
    public virtual void TearDown()
    {                      
    }
}

Now when you inherit from Specification, you are forced to provide implementations of EstablishContext() and When().  I also wrap the When() in a try/catch so that you can do things like Assert.WasThrown(...);

The BDD Journey

I'm still a BDD newbie, but I'm now sold on it's organizational structure and it's ability to connect tests back to actual requirements of the system.  It provides a clear picture of how to group tests & setup behaviors and combined with the new RhinoMocks AAA syntax, makes my tests much easier to read.

The tipping point for me was attending Raymond Lewallen's BDD talk at Tulsa TechFest.  I recorded the first hour of it, and it's available up on Viddler.  I hope this helps more people understand BDD.  I've seen presentations like this before, but it took some practice (and failure!) on my own time to start to see some speed bumps.  With this knowledge I was able to ask some direct questions and get (gasp!) direct answers.

While I cannot justify going back and changing 500 or so unit tests to this form, I will ensure that I start writing BDD style specifications for my new production code moving forward.

#1 Lee Brandt avatar
Lee Brandt
10.12.2008
4:23 AM

I, too, am a newbie to BDD. I am totally sold on its ability to help me test the business value I am adding when building a system. I am still a littel unsure of the way to use it to drive design. My tests in TDD are very granular and force my design to be decoupled, but TDD does very little in the way of verifying business value added, and the BDD style tests seem to be more coarse-grained. Is there value in doing both? Writing BDD to verify that the business features are added and working as expected and using TDD to drive out the technical design?Like I said, I'm still very new to it, so I am trying to understand how it should work.~Lee


#2 Matt Hinze avatar
Matt Hinze
10.12.2008
9:55 AM

Great post.Ben, I'm along side you here, just starting to get this stuff.@Lee - another thing about 'fixture per class' is that just the first step of writing the test fixture class name begins implicating design.I don't have the answer yet on the coarse-grained nature of bdd but I'm starting to trust it more even for testing the smallest units - I think I'm just not used to "reading" specs as documentation like i was with classic tdd.


#3 Darrell Mozingo avatar
Darrell Mozingo
10.12.2008
10:16 AM

Nice article. Your point about only having one TestFixture per SUT really made the rational for BDD start sinking in for me.One thing I've always wondered when reading these BDD posts, though, is if there's any standard way to organize your specifications? From your example, would I keep the "when_invoking_edit_action_as_authorized_user" spec alone and name my file after it? when_invoking_edit_action_as_authorized_user.cs? Seems sorta long and difficult for a file name. Is it more common instead to stick all the specs related to a certain SUT into a single file and name the file after that SUT (something like sticking your spec in a file named "PostControlSpecs.cs" and throwing in any PostController related contexts as separate classes)?Thanks!


#4 Ben Scheirman avatar
Ben Scheirman
10.12.2008
10:41 AM

@Darrell - I'm also concerned about how many specs I should have in a single file...often times the specs will describe more than one system, and the specs for those should try to isolate the system (and thus fit into a file specific to the class under test).This might prove to be hard though.If I have any epiphanies I'll post about it :)


#5 Robby Gregory avatar
Robby Gregory
10.12.2008
10:41 AM

Great post Ben.I too attended this session at Tulsa TechFest, and I also had somewhat of an epiphany because of it.I had somewhat dipped my toe in the water when it comes to BDD, but Rays presentation really pushed me over the edge.I left there wanting to take this back to my organization.Thank you very much for posting the video so that I can let Ray explain this to my coworkers who did not attend TechFest instead of listening to my lackluster representation of it.


#6 J.P. Hamilton avatar
J.P. Hamilton
10.13.2008
12:27 PM

Ben, I relate to everything you said in the beginning of this post. It's good to know that people are having the same struggles as myself. I haven't delved too deeply into BDD, but this post has sure motivated me to do so. My tests are ending up just how you describe. It truly makes refactoring a PITA and is not only counterproductive, but de-motivating as well. Thanks for a great post!


#7 Rob Miles avatar
Rob Miles
10.17.2008
8:27 AM

Hi Ben,Interesting article, thanks.Does this imply that you have a test fixture class for every logical path through your application?Rob


#8 Ben Scheirman  avatar
Ben Scheirman
10.17.2008
8:49 AM

@rob:different context == different fixture, so yes.


#9 Afif avatar
Afif
11.26.2008
10:36 PM

Hey Ben,

Your method 'When()' which does the actual test makes a call to _mycontroller.edit(5). Lets assume here that 5 is the id by which a post is fetched., so now I want to (for sake of this example) also test if I can get posts by id 7,3 and 23. The rowtest attribute would suffice for such a test where I pass these constants to the test method as params.

But in our case, how would this be acheived? With the design you have, I would have to write a new Testfixture for each of these tests. But all these tests still have the same context, and checking the same behaviour. I should be able to replicate the behaviour for different inputs without having to set it up all over again.

Your thoughts?

-aFiF


#10 benscheirman avatar
benscheirman
11.26.2008
11:41 PM

Afif, that's a good question. One, I'd really question the value of testing arbitrary numbers. Testing happy path, bad data and edge cases is a good idea though.

You could probably tweak this to put When inside of a test, then call your various asserts on it. Or better yet, call your When() with the input desired from your test... that way there's very little duplication.


#11 Michael Knopf avatar
Michael Knopf
12.05.2008
1:41 PM

Great article, this is the first I've read on BDD and it looks promising. Thanks for you contribution.


#12 Michael Knopf avatar
Michael Knopf
12.09.2008
12:46 PM

All, I attended the Tampa Code Camp (www.tampacodecamp.com) and Sean Chambers gave a presentation on BDD, he wrote up a great article on it (http://www.lostechies.com/blogs/sean_chambers/archive/2008/12/07/starting-with-bdd-vs-starting-with-tdd.aspx) He uses ReSharper and SpecUnit which dramatically reduce the overhead in laying out the test classes.

@Afif, Sean addresses the concerns that you brought up about "With the design you have, I would have to write a new Testfixture for each of these tests." and shows how BDD actually makes code reuse in your tests much easier.


#13 Peter Seale avatar
Peter Seale
2.16.2009
1:49 PM

Yeargh, that Specification base class is exactly what I needed. Saturday.

But I'd like to think that by thrashing around a lot it helped in some way. Hmm.

Also I probably will have to adopt the thrown exception property, because it looks like there's no other way to first call the When() and check for exceptions in the [Test] method. I'll look around for other samples.

Also I will say that, while I respect JP Boodhoo and whatnot, there's no way I'm adopting his BDD setup: blog.jpboodhoo.com/.../MoreNewConventi