Wednesday, December 09, 2009

Leaping into BDD

As this title says Leaping into BDD I have plunged in to learn about and use BDD.  My experience with it is new and I don’t claim to know everything about BDD.  With this said if you have some more insight or explanations you want to indulge us with please don’t hesitate to broaden our knowledge by sharing yours. 

We have all heard of Test Driven Development (TDD) and many of us have used it.  I’m going to tell you about a newer type of development out that in my opinion produces a much easier flow.  The new development is called Behavior Driven Development or BDD.  BDD is exactly as it is named, it specifies your code from a behavioral approach.  By the time we start to code we know what we want the project to do because we have already thought this out by now.  So using BDD we can write specifics to guide our code.  As an example, lets say we are writing a notepad application.

Our specifics may look something like this:

Open Text File for Editing
    - File Open Dialog with Filters
    - Content of file is extracted and placed onto Rich Text Box
Save Changes
    - Collect text and save back to opened file
Close Notepad
    - Check for changes made
        - if changes prompt user
    - Cleanup code
    - Close application

These specifications are pretty straight forward; we are thinking like a human and how a human would use our application.  In these scenarios we would have the option to open a file; OK, after the file is opened what do we do? Well according to our specs we can save the file, and we can close the application.  During a close we are showing that our application should check for changes and prompt the user if any exist.  Handle our cleanup of code and resources, then close the application.  Pretty straight forward right?

To get started using BDD you must first get the following:

- MSpec (Machine.Specifications)
- TestDriven.Net Personal/Professional (Not Required but good for running your test)

 

Once you have downloaded MSpec and unzipped, you will have something like this:

MSpecDir

 

Double click on the build batch file to run the compiler against the code and output the assemblies we need.
Note: You may receive errors on Resharper if you don’t have that installed, don’t worry about those.  You may also have to open the solution in Source and manually compile.

Lets get started by opening Visual Studio, I’m going to create a class called BDD to hold our properties.  But first before I do that I need to know my specifications, lets do that:

BooleanProp is true
- DoubleProp must be greater than 0
- IntegerProp must be 0
BooleanProp is false
- DoubleProp must be 0
- IntegerProp must be greater than 0

So I know I will have 3 properties, BooleanProp, IntegerProp, and DoubleProp.  First context I am saying if the BooleanProp is set to true then DoubleProp must be greater than zero and IntegerProp must be zero.  With this said, lets create a class with these properties.

public class BDD
{
private int _IntegerProp;
public int IntegerProp
{
get { return _IntegerProp; }
set
{
_IntegerProp = value;
}
}

private double _DoubleProp;
public double DoubleProp
{
get { return _DoubleProp; }
set
{
_DoubleProp = value;
}
}

private bool _BooleanProp;
public bool BooleanProp
{
get { return _BooleanProp; }
set
{
_BooleanProp = value;
}
}
}


Now that I have my class I’m going to write out the specifications in code.  I start by created a class, ill call it BDDSpecifications.



using Machine.Specifications;

public class BDDSpecification
{
/*
* BooleanProp is true
* - DoubleProp must be greater than 0
* - IntegerProp must be 0
* BooleanProp is false
* - DoubleProp must be 0
* - IntegerProp must be greater than 0
*
*/

[Subject("BooleanProp is true")]
public class When_BooleanProp_is_true : BDDClass
{
Because of = () =>
{
testClass.BooleanProp = true;
};

It should_have_doubleProp_GreaterThanZero = () =>
{
testClass.DoubleProp.ShouldBeGreaterThan(0);
};

It should_have_integerProp_equal_zero = () =>
{
testClass.IntegerProp.ShouldEqual(0);
};
}

[Subject("BooleanProp is false")]
public class When_BooleanProp_is_false : BDDClass
{
Because of = () =>
{
testClass.BooleanProp = false;
};

It should_have_doubleProp_equal_zero = () =>
{
testClass.DoubleProp.ShouldEqual(0);
};

It should_have_integerProp_GreaterThanZero = () =>
{
testClass.IntegerProp.ShouldBeGreaterThan(0);
};
}

public abstract class BDDClass
{
protected static BDD testClass;
Establish context = () =>
{
testClass = new BDD();
};
}
}


Here you can see my specifications in code.  I have an abstract class, this is not required but prevents me from having to create the BDD class for each test.  Notice my classes inherit the BDDClass.  Looking at the code you might be a little nervous, don’t be.  The code is actually very simple,



Because of Something, It should have/do this, It should have/do that, etc, etc, etc.



 



If you did not install the TestDrive.Net, no problem.  When you compiled the source for MSpec an executable was generated called mspec in the build output.  Open your Tools->External Tools, click on Add.



Parameters:



Title: Add a title

Command: The path to the mspec.exe file


Arguments: $(TargetName)$(TargetExt) --html $(ProjectDir)Reports\report.html  
(Note: By adding the –-html “Report Path” an html file will be created for you with the results.  The html part is not required.)


Initial Directory: $(BinDir)



image



 



Now that you have your specifications all laid out, lets run this.  I'm going to run using the MSpec option.




My Results:


image



I have 2 that failed which I expected.  To make these pass I need to make some changes.



public class When_BooleanProp_is_true : BDDClass
{
Because of = () =>
{
testClass.BooleanProp = true;
testClass.IntegerProp = 0;
testClass.DoubleProp = 2;
};







I made a simple change to both classes and got the following results:





image



 



All specifications passed!! The report generated is a nice addition that can be printed and handed over to your PM.



 



Happy Coding…

3 comments:

Anonymous said...

It's not actually a specification as it doesn't assert anything about the behavior. You need to specify something your code is actually going to do. Making a spec pass by setting it true in the specification is a bit pointless.

Gary Cox said...

Interesting, everything I have read so far says "Specification" and "Context" and not tests. I agree that making the spec pass by setting the values is pointless, I wanted to demonstrate a passed run. Thanks for your feedback.

Anonymous said...

I believe the usage of specification and context are an attempt to get developers to think like analysts, it is just essentially a wrapper of unit tests.

Make the run pass by implementing the code :P

You may want to move the specifications up a level, start with stories, to avoid defining the behavior at code level (which can be done with unit testing) rather than system level.

 
Creative Commons License
Blogged Information and Code is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.