Unit level BDD using SubSpec

In the BDD (Behavior Drive Development) there are two groups of frameworks. The first group contains tools such as SpecFlow and nBehave to name a few all of which uses (at least) two files – one contains the scenarios/behaviors in plain English (more or less) and the other the code that makes it all happen.
I want to discuss the other type – the one that contains “unit level” BDD. Unlike the first group the behaviors are written inside the code where only us developers can read them – you can always try to convince your product/marketing manager to read your code but it tends to go downhill from there…
The reason I’m telling you this is that this week I discovered a very interesting tool – SubSpec. It uses extension methods on System.String, lambdas and some nifty XUnit tricks to make BDD accessible and easy to use.

Getting started

Using SubSpec is simple. After creating a new project add a reference to SubSpec using NuGet:
image
Adding SubSpec via NuGet will also make sure that you have the required dependencies - XUnit and XUnit.Extensions:
image
And you’re ready to go…

Writing the first behavior

Like any other test SubSpec test is divided into three parts – Context, Action and Verifications.
The entry point to all three is a string.
Writing a new test is done by following these four steps:
  1. Add Specification attribute to the test method
  2. Use Context to initialize the class under test
  3. Use Do to perform an action
  4. Use Assert to test for success/failure
[Specification]
public void StoreWithOneProduct()
{
    const string productId = "prod-1";

    var store = default(Store);

    "Given a new store".Context(() => store = new Store());

    "with one product".Do(() => store.AddProduct(productId));

    "The store has that product".Assert(() => Assert.True(store.HasProduct(productId)));
}

Because SubSpec is built on top of XUnit any runner that is able to run XUnit tests can run these specifications. I’ve used the freely available XUnit Runner for VS2012 and got this results:

image

As you can see the strings used are shown as the test name making it easy to understand what is being tested.

Wait there’s more

One problem I always have is tests that test more than one assertion. The problem is that if one assertion fail we cannot know what is the status with the other assertions being tested. SubSpec has a cool solution – run each assertion as a different test:

[Specification]
public void StoreWithOneProduct()
{
    const string productId = "prod-1";

    var store = default(Store);

    "Given a new store".Context(() => store = new Store());

    "with one product".Do(() => store.AddProduct(productId));

    // Usually this line would cause the line after it to fail
    "Customer can purchase that product".Assert(
        () => Assert.True(store.BuyProduct(productId)));

    "The store has that product".Assert(() => Assert.True(store.HasProduct(productId)));
}

image

SubSpec has actually run two separate tests one for each assertion. But in case you want a proof – let’s write a test with a failing assertion:

[Specification]
public void StoreWithOneProduct()
{
    const string productId = "prod-1";

    var store = default(Store);

    "Given a new store".Context(() => store = new Store());

    "with one product and product is purchased".Do(() => 
        {
            store.AddProduct(productId);
            store.BuyProduct(productId);
        });

    // this is wrong - stop copy/pasting your tests :)
    "Customer can purchase that product".Assert(
        () => Assert.True(store.BuyProduct(productId)));

    "The store does not have that product".Assert(() => Assert.False(store.HasProduct(productId)));
}

And the result of this test run is:
image

The 2nd assertion passed since it was run independently from the 1st failing assertion.

Running all of the assertions in one test

What if you do need to run all of the assertions in one test? Simple, use Observe keyword that cause all of the assertions created within it to run in the same context.

[Specification]
public void PushSpecification()
{
    var stack = default(Stack);
    "Given a new stack".Context(() => stack = new Stack());

    string element = "first element";
    "with an element pushed onto it".Do(() => stack.Push(element));

    "the stack is not empty".Observe(() => Assert.False(stack.IsEmpty));
    "the stacks Top is the pushed element".Observe(() => Assert.Equals(element, stack.Top));
}


SubSpec Assert


And yes – you can mix Assert and Observe in the same test – and Godspeed on that.

Is that All

Yes!

This is all you need to know in order to use SubSpec, there’s nothing more. It’s simple compact and works. Johannes Rudolph - the creator of SubSpec has taken pains not to add anything else to his frameworks keeping it simple and very small.
But so far I failed to find any limitation that prevents me from using SubSpec and XUnit to quickly add behaviors to my tests.

Happy coding…

Labels: , , ,