Make BDD Your BFF

symmetry I just can’t say enough about BDD (Behavio[u]r Driven Design) and how it’s helping me with an application I’m writing (which you’ll know all about soon enough). I’ve stopped myself from soooo many stupid errors it’s unbelievable – and since I’ve been writing so many opinion posts lately, I thought I’d do an end to end BDD post for the masses.

symmetry

I just can’t say enough about BDD (Behavio[u]r Driven Design) and how it’s helping me with an application I’m writing (which you’ll know all about soon enough). I’ve stopped myself from soooo many stupid errors it’s unbelievable – and since I’ve been writing so many opinion posts lately, I thought I’d do an end to end BDD post for the masses.

Step 1: Get the Bits You’ll Need

Download Aaron Jensen’s Machine.Specifications (MSpec) from here (click on Download) and put it on a drive on your machine. If you’re Git-savvy, then just clone the repo (git://github.com/machine/machine.specifications.git). This is (in my opinion) by far the best test-running framework out there.

Step 2: Download and install Test-driven .NET (TD.NET)

You don’t need to do this if you have ReSharper – but if you want to debug the tests (err – specs) that you write, you’ll need a way to invoke it. TD.NET is your best friend – got get it – it’s free for personal use.

Step 3: Make TD.NET and MSpec friends

When you download MSpec, Aaron and friends are good enough to create a BAT file that hooks TD.NET and MSpec up. You only need to run this once and what you’re doing is telling TD.NET “When I tell you to run a test for MSPec – do this”. ‘

If you open up the MSpec download folder there is another folder in there called “Distribution”, in there is one called “Specifications” and you’ll see the .bat file that you can double-click. For some reason I couldn’t get this to work right and TD.NET wouldn’t work with the MSPec stuff. If you’re in this boat with me – then create a .reg file and add this text, then execute it by double-clicking:

Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Software\MutantDesign\TestDriven.NET\TestRunners\MSpec] "Application"="" "AssemblyPath"="[DOWNLOAD PATH]\\machine.specifications\\Build\\Debug\\Machine.Specifications.TDNetRunner.dll" "TargetFrameworkAssemblyName"="Machine.Specifications" "TypeName"="Machine.Specifications.TDNetRunner.SpecificationRunner" @="5" 

 

Name the file whatever you want with a “.reg” extension – double click and you’re off.

Step 4: Hook MSpec up to Visual Studio

I don’t like running all the tests using TD.NET – it’s too slow. Instead I use the test runner Aaron and team have created as it’s screaming fast. To do this I like to use External Tools (Tools | External Tools) and I add the setting as you see below:

extools 

Note that the command box points to the ConsoleRunner in the Build\Debug folder. If you want an HTML report to be output to your project – you can do it (and I recommend it) by adding this as your arguments:

$(TargetName)$(TargetExt) --html "$(ProjectDir)\Report.html"

This will tell MSpec to output a file called “Report.html” that you can share with others. More on this below…

Step 5: Create a Class Lib Project and Add References

Easy enough – File | New Class Library, then add the following references:

  1. Machine.Specifications
  2. Machine.Specifications.NUnit
  3. nunit.framework

If you don’t have nunit – well go get it!

This might seem like a lot of steps – the truth is most of them are one-time setup steps that will be easier next time.

All set? Let’s write some SPECS!

Writing Specs (Tests) With MSpec

The first thing to remember when using MSpec is that you’re testing the behavior of whatever it is you’re working with – not necessarily the functionality. The goal of writing these weird looking things is to have them readable by your client – “Executable Specifications” if you will.

So, when you sit down to crank these things out, change your mind a bit and realize you are (literally) writing “specs” for others to read – not tests that you want to pass (that’s a developer thing). Unfortunately this means that you’ll also be writing some funky looking code – but it’s all worth it.

Example – Object Overrides

You’re building a blog for your client, and you’ve discussed URLs. They want their posts to be identifiable by a “pretty URL” and it should be based on a title. You think the typical WordPress URL might work so you propose this:

  1. A post will have a “slug” which will be based on the title, minus any URL-prohibited characters
  2. The post URL will use the slug and the year, month, and day of the post to uniquely identify the post.

You now have some requirements and you can write your Post class. Being a good geek you decide to override the core .NET object methods so you can test your Post a bit easer:

public override bool Equals(object obj) {    if (obj.GetType() == typeof(Post)) {        Post compare = (Post)obj;        return compare.Slug.Equals(this.Slug, StringComparison.InvariantCultureIgnoreCase);    } else {        return base.Equals(obj);    }}public override string ToString() {    return Title;}public override int GetHashCode() {    return ID.GetHashCode();}

 

All good –this should work nicely – or will it? We should probably have written the specs first (in BDD they’re “specs”, not tests) – but we haven’t even written one yet. Let’s come back to this.

Bending C# To Your BDD Will

C# wasn’t meant for this kind of gymnastics – but it’s what we got. If you ever use RSpec with Ruby you’ll know why this stuff was written this way (Ruby/RSpec are amazingly clean and simple to read) – but for now just know that I have some tricks for you – just hang tight.

The idea is that you want to use a thing called a “Context” which you will test the behavior of. In our case, we want to test our Post – but that Post will have certain settings on it so we know what we’re testing. This is our Context and in MSpec you write the Context as an abstract class which must be inherited:

    public abstract class with_null_post {        protected static Post post;        Establish context = () => {            post = null;        };    }

Yes – freaky. Very freaky. But I can explain this…

First – this is a class – an abstract class which means you can’t instantiate it directly. It’s sort of like an Interface, but not.

Second – the name. It’s written that way to help the MSPec reporting bits output something groovy and awesome. It goes against every fiber of most people’s C# super powers – but just stick with it. It’s not such a big deal after a while and there’s also a reason it starts with “with_”. I’ll get to that.

Next – WTF is “= () =>”. I’ll tell you –but I’ll also tell you not to worry about it. It’s an Anonymous Delegate – basically a “method on the fly”. The syntax here is “Set the Establish delegate equal to this code I’m about to write”. Establish is used by MSpec when you run your tests – just accept it, it won’t hurt you.

Finally – we have our Post and it’s static. This is a concession Aaron and team had to make for the final result – and it’s damn worth it, as you’ll see.

In summary form what we have here is something that makes sense when spoken aloud:

“Establish with_null_post as a post=null”

Writing Your First Spec

You can’t write a Spec without a Context – and we have one of them now. At this point you’re saying “Big F***in Deal” and hopefully I’ll convince you that it actually is. When writing specifications they have to be founded on a “context” if you will – and if that doesn’t make sense, consider this statement:

“When a Post is created it should default the publication date to today and the status to draft”.

This is behavior – and it’s something we should spec out, and we can use our null Post to do it since that will be our Context (creating a Post from nothing).

Actually, if you slice and dice that sentence a bit you’ll see we have two bits of behavior – setting the publish date and the status – so we can use MSPec to define this (again, with crunchy syntax):

[Subject("New Post")]public class when_creating: with_null_post {    Because of = () => {        post = new Post();    };    It should_default_published_date_to_now;    It should_default_status_to_draft;}

 

Yes – whacky syntax again – but I promise you will get used to it. It’s worth it, especially when you see the result…

First is the Subject attribute – this is purely for reporting and you can send in a type or a string – whatever makes your reports more readable.

Second – you’ll notice this is a class that inherits from the Context – this is intended to be readable (as it is here). If it doesn’t make sense – neither does your spec.

Because is another delegate and “of” is just magic – but it’s meant to be readable. What this code is saying is “because of this action on the Context” and in it’s curly bits you need to put the action that will be tested. In this case – it’s creating a new Post.

The following lines are just crazy. Again – they are delegates (“It” is another special test delegate) but they’re formed the way they are to be readable – as you’ll see in a second.

This is a spec – and it’s executable and when I run it using the MSpec runner I setup above, this is what i see:

Specs in MyProject.Specs:New Post, when creating¯ should default published date to now (NOT IMPLEMENTED)¯ should default status to draft (NOT IMPLEMENTED)

And that’s the payoff. What this type of testing allows us to do is to define behaviors for our classes without writing a lick of code – and not worrying about compiler issues. In this case our spec is reporting a very accurate answer – we haven’t done anything yet…

Being good BDD coders we can go nuts with this – setting up specs to code against, and the filling out the tests as we go.

And that, friends, is BDD.

Getting Your First Spec to Pass

This next part is where we actually test. Let’s say you write the code for your post – setting up the default values in the constructor etc. Now you want to test that functionality to make sure your spec is valid:

    [Subject("New Post")]    public class when_creating : with_null_post {        Because of = () => {            post = new Post();        };        It should_default_published_date_to_now = () => {            post.PublishedAt.ShouldBeGreaterThan(DateTime.Now.AddSeconds(-1));            post.PublishedAt.ShouldBeLessThan(DateTime.Now.AddSeconds(1));        };        It should_default_status_to_draft = () => {            post.Status.ShouldEqual(PostStatus.Draft);        };    }

Notice here that we’ve filled out the “It” stuff to include some tests. These “tests” use a set of Extension Methods that come with MSpec to, again, make things more humanly readable. I think my date tests there could use some love, but if you read them I think you get the idea.

Now, when I execute these tests I see a nice result:

Specs in MyProject.Specs:New Post, when creating¯ should default published date to now¯ should default status to draftContexts: 1, Specifications: 2

 

I can actually print that out and take it to my client or manager and we can read it together (along with the 50 or so NOT IMPLEMENTED specs) – and we’ll be right on the same page.

What’s that? Don’t like the weird text layout? That’s OK – here’s a snippet of the HTML output that I mentioned above, with all the specs I’ve written for my blog engine I’m making for myself:specs

 

This is being output in my project directory – but I could easily put it in a network share or somewhere else – it reads very well!

Why You Should Care

Remember where we started with all of this with the object overrides? Well I decided to write it first because hey – it’s just object overrides and I’ve written them before and I’ll test them anyway. Let’s consider the specs we’ll write:

[Subject("New Post")]public class object_overrides : with_null_post {    protected static Post post2;    Because of = () => {        post=new Post();    };    It should_have_title_as_main_identifier;    It should_be_equal_to_other_post_with_same_slug;    It should_have_same_hash_code_as_ID;}

As I’ve been mentioning – the main goal of BDD is to get you to think about Behavior of the application, not just functionality. You might think that that’s great for the results – why the hell do I need to see it in my code?

The answer is right in front of you – are these specs correct? Let’s revisit the requirements:

  1. A post will have a “slug” which will be based on the title, minus any URL-prohibited characters
  2. The post URL will use the slug and the year, month, and day of the post to uniquely identify the post.

If I was writing Unit Tests I don’t think I would have ever caught the problem – but maybe that’s just me. Writing “specs” that my client can understand – well that threw the problem right in my face: the equality test was all kinds of wrong (no year, month, day). How do I know? Because it’s the way I wrote it – AFTER I wrote the code. Oops. Had I written the failing specs first, my client could have reviewed the specs and “accepted” them – then I would have been quite sure of what it was I was doing!

Let’s try that again – this time I’ll tweak it so my client knows what’s going on:

[Subject("New Post")]public class identifiers_and_equality : with_null_post {    protected static Post post2;    Because of = () => {        post=new Post();    };    It should_have_title_as_main_identifier;    It should_be_equal_to_other_post_with_same_slug_year_month_day;    It should_use_id_as_system_identifier;}

 

Here’s what’s changed:

  1. I renamed the Concern to make a bit more sense to the client (“identifiers and equality”).
  2. I changed the second spec to include year, month, and day in the equality test
  3. I changed the last spec to get rid of “hash code” and used something my client could understand

The Payoff

This is the goal of BDD – to get you to think a bit more like your client, and to write tests that fit a bit more into what they see. This will help you to design and build your application in a much more client-specific way and ultimately save you LOADS of time. I can attest to this in all kinds of ways – taking my time and writing specs first is much, much faster.

Tips and Tricks

By far the biggest hurdle to BDD is the syntax. It took me a while to get it and rather than type all that unfamiliar syntax out, I decided to create some code snippets instead. You can download them here. To use them, download to your hard drive somewhere and open up your Code Snippets Manager (VS 2008 – Tools | Code Snippets Manager) and click the “Add” button. Select the snippets folder and then click OK.

To use them, type “context” and TAB – TAB and you’ll have your context. “spec” will create a spec template, and “spec0” will create one that’s not implemented.

If you want to see a screencast on BDD and MSpec, you can watch this one here that I did a few months back.