Garry Shutler

BDD from scratch – Build your own framework

September 6, 2009 · 5 min read

Behavior-Driven Development (BDD) is a higher level of unit testing, it creates better documentation of your system by recording its intent which makes your system easier to learn for new developers and relearn for when you revisit your code further down the line.

I’m going to show you how to build your own BDD testing framework on top of a vanilla unit testing framework. For my example I’m going to use NUnit but I’ve applied the same principles with MbUnit, xUnit, and it should work with any other unit testing framework too.

What’s good about doing things this way, as opposed to using a purpose built BDD framework like MSpec, is that it allows your tests to exist and be run side-by-side with traditional unit tests. This can ease the migration to the new style if you’re all moving over to it and it lets you write tests in the BDD style for some parts of the system whilst sticking with the traditional unit style for other parts. Though I’ll be amazed if you don’t end up writing all your tests in the BDD style as everyone I’ve shown it to loves it.

I’ll show you how to create the basis for your own BDD framework and I’ll give an example of how a BDD style test looks in comparison to a traditional unit test.

What is BDD all about?

The core concept of BDD is Given-When-Then (often referred to as Arrange-Act-Assert):

Herein lies another fundamental difference with BDD, you will create a test fixture per scenario rather than a fixture per class as it common with traditional unit testing.

The basis of our framework

The way to transform your standard unit testing framework to a BDD one is to use an abstract base class. This will modify how your tests are run, giving you the ability to specify your Given, When, and corresponding Thens separately from one another:

using NUnit.Framework;

namespace RobustSoftware.BddFromScratch.Framework
{
    [TestFixture]
    public abstract class BehaviourTest
    {
        [TestFixtureSetUp]
        public void Setup()
        {
            Given();
            When();
        }

        protected abstract void Given();
        protected abstract void When();
    }
}

This means that the contents of your Given and When methods will be run once before each of your methods decorated with the Test attribute are run. This is important as it means that all your assertions should not have side effects (this should be the case already). You will use it in a test class by doing something like this:

using System;
using NUnit.Framework;

namespace RobustSoftware.BddFromScratch.Framework
{
    public class ExampleBehaviour : BehaviourTest
    {
        protected override void Given()
        {
            // the system is setup in a certain state
        }

        protected override void When()
        {
            // a defined action is performed on the system
        }

        [Test]
        public void ThisAssertionShouldBeSatisfied()
        {
            Assert.IsTrue(true);
        }

        [Test]
        public void AnotherAssertionShouldBeSatisfied()
        {
            Assert.IsTrue(true);
        }
    }
}

Notice that the intent verified by each assertion is documented in the name of the test method. This means that when you run your tests, the name of the tests themselves show you when was meant to happen rather than the error messages that most people forget to add to their assertions.

Another thing that I like to do but is entirely optional is alias the Test attribute to be able to use a Then attribute with the same behaviour:

using NUnit.Framework;

namespace RobustSoftware.BddFromScratch.Framework
{
    public class ThenAttribute : TestAttribute
    {
    }
}

This just changes the original example test slightly so it’s more obvious how the Given-When-Then style is being applied:

using System;
using NUnit.Framework;

namespace RobustSoftware.BddFromScratch.Framework
{
    public class ExampleBehaviour : BehaviourTest
    {
        protected override void Given()
        {
            // the system is setup in a certain state
        }

        protected override void When()
        {
            // a defined action is performed on the system
        }

        [Then]
        public void ThisAssertionShouldBeSatisfied()
        {
            Assert.IsTrue(true);
        }

        [Then]
        public void AnotherAssertionShouldBeSatisfied()
        {
            Assert.IsTrue(true);
        }
    }
}

Converting a traditional test

I’ll start of with what I think to be a pretty standard unit test that I found in the source of OpenRasta:

[Test]
public void change_after_creation_creates_new_object_with_the_same_properties()
{
    var binder = new KeyedValuesBinder("customer", typeof(Customer));

    binder.SetProperty(
        "username",
        new[] { "johndoe" },
        (str, type) => BindingResult.Success(str)
    );
    binder.BuildObject();

    binder.SetProperty(
        "firstname",
        new[] { "john" },
        (str, type) => BindingResult.Success(str)
    );

    var customer = (Customer)binder.BuildObject().Instance;

    customer.Username.ShouldBe("johndoe");
    customer.FirstName.ShouldBe("john");
}

As I see it this, like many other traditional unit tests, is already split into the Given-When-Then style logically:

[Test]
public void change_after_creation_creates_new_object_with_the_same_properties()
{
    // Given - the context we are establishing in the system
    var binder = new KeyedValuesBinder("customer", typeof(Customer));

    binder.SetProperty(
        "username",
        new[] { "johndoe" },
        (str, type) => BindingResult.Success(str)
    );
    binder.BuildObject();

    binder.SetProperty(
        "firstname",
        new[] { "john" },
        (str, type) => BindingResult.Success(str)
    );

    // When - the action we are performing on the system
    var customer = (Customer)binder.BuildObject().Instance;

    //Then - checking the system ends up in the desired state
    customer.Username.ShouldBe("johndoe");
    customer.FirstName.ShouldBe("john");
}

So splitting it physically into the separate sections is not much of a leap:

public class ChangingObjectAfterCreation : BehaviourTest
{
    private KeyedValuesBinder binder;
    private Customer customer;

    protected override void Given()
    {
        binder = new KeyedValuesBinder("customer", typeof(Customer));
        binder.SetProperty(
            "username",
            new[] { "johndoe" },
            (str, type) => BindingResult.Success(str)
        );
        binder.BuildObject();
        binder.SetProperty(
            "firstname",
            new[] { "john" },
            (str, type) => BindingResult.Success(str)
        );
    }

    protected override void When()
    {
        customer = (Customer)binder.BuildObject().Instance;
    }

    [Then]
    public void UsernameShouldBeJohnDoe()
    {
        Assert.AreEqual("johndoe", customer.Username);
    }

    [Then]
    public void FirstNameShouldBeJohn()
    {
        Assert.AreEqual("john", customer.FirstName);
    }
}

I’ve dropped the use of the assertion extension methods that Sebastien Lambla was using, but otherwise the actual test code has remained the same.

The physical separation of the logical parts of the test makes the test easier to follow and the assertions at least as explicit even without the extension methods being used.

The fact that BDD makes the separation many people already make when writing traditional unit tests more explicit is why I and other people like it. It makes your tests more readable as you can often skim the Given section as that should be conveyed by the name of the entire fixture. That leaves you to see what method is being called and the assertions being made. These are often now more descriptive as they are given a method name.


Photo of Garry Shutler

Hey, I’m Garry Shutler

CTO and co-founder of Cronofy.

Husband, father, and cyclist. Proponent of the Oxford comma.