Click here to Skip to main content
Click here to Skip to main content

Getting started quickly with BDD (Behaviour driven development) in .Net

, 11 Aug 2010
Rate this:
Please Sign up or sign in to vote.
This article will give you a brief introduction on how to get started with BDD (Behaviour driven development). It is my second attempt for such an article.I will show you the complete workflow on how to write an app using BDD in .Net.

Introduction

This article will give you a brief introduction on how to get started with BDD (Behaviour driven development). It is my second attempt for such an article.

I will show you the complete workflow on how to write an app using BDD in .Net.

Background

Small note: if you want to know why you should use BDD, I would like to refer you to another one of my articles: The advantage of using BDD over TDD

Today I finally managed to upgrade my Aubergine BDD framework to v0.1.

This is now starting to get really usable (I've been dogfooding it for a while now).

This release contains the following changes:

  • Very basic support for NUnit
  • Build script using JeremySkinner's Phantom build engine
  • Properly named namespaces/dll's (i.e. no more Be.Corebvba.*)

In this spirit I decided to write a small tutorial on how to do BDD development in .Net, so maybe some people might find some use for this !!

Requirements

In the spirit of OSS development, I am going to show you how to develop an application using BDD together with SharpDevelop, an OSS IDE for .Net. You are offcourse free tu use any other IDE (i.e. Visual Studio, Monodevelop, ...)

Sharpdevelop

If you want to continue this tutorial using SharpDevelop, you can download it here (I used 3.2):

http://www.icsharpcode.net/opensource/sd/download/#SharpDevelop3x

In other IDE's the steps will probably be more or less the same, except that your screenshots will probably not match.

Aubergine

Next to this you also need my Aubergine BDD framework. You can download it here:

http://github.com/ToJans/Aubergine/zipball/master

If you want to, you can also take a quick peek at the source here:

http://github.com/ToJans/Aubergine

Getting started

Create a new empty solution in sharpdevelop, name it MyFirstBDD

Right click on the solution, and add a new project

Choose class library, and name it MyFirstBDD.Tests

Define your specs

This is a very simple example, but IRL you will have multiple txt-files with multiple stories and scenarios describing as much functionality as possible 

Right-Click on the MyFirstBDD.Tests, and choose add a new file. For the file-type, select empty file, but name it specs.txt, and add the following content: 

Define my own library
 using MyFirstBDD.Tests.MyOwnLibrary 
  from MyFirstBDD.Tests.DLL 

Story Manage my books 
  Is about my own library 

  As an owner of books  
  I want be able to search for a keyword or part of the author/name of the book in my current books 
  So that I can avoid buying the same book multiple times. 


  Given I have the following books 
  +---+---------------------------------------+------------------------+--------------------+
  | Nr| Title                                 | Author                 | Keywords           |
  +---+---------------------------------------+------------------------+--------------------+
  | 1 | Einsteins Schleier                    | Anton Zeilinger        | Quantum physics    | 
  | 2 | De kunst van het overtuigen           | Hans Christian Altmann | Business,NLP       |
  | 3 | Harry Potter and the return of Peewee | Joske Vermeulen        | Fun, Eglantier     |
  | 4 | Teititatutei                          | Joske Vermeulen        | Geen fun, Eglantier|
  | 5 | Een twee drie                         | Joske Vermeulen        | fun, Eglantier     |
  +---+---------------------------------------+------------------------+--------------------+ 

  Scenario search for a single matching word 
    When I search for "een" 
    Then it should show 1 book 
     And it should not return book nr 4 

  Scenario search for a single matching word with a wildcard 
    When I search for "*een" 
    Then it should return 2 books 
     And it should return a book with the title "Teititatutei"  

This defines what we want our app to do.

Define your BDD context 

Now the we know what we want, we will write our spec executors. These are simple implementations which will not compile yet (since we do not have a program yet). 

These will help you to think about your interfaces you will expose from your program.

I then add the minimum amount of code needed to get it compiled (i.e. class Book and interface ILibrary).

We identify all possible Given/When/Then steps, and make sure we gave some code for it, and also define the necessary interfaces:

using System;
using System.Collections.Generic;
using Aubergine.Model;
using System.Linq;

namespace MyFirstBDD.Tests
{
    public class Book
    {
        public int Id {get;set;}
        public string Title {get;set;}
        public string Author {get;set;}
        public IEnumerable<string> Keywords {get;set;}
    }
    
    public interface ILibrary
    {
        IEnumerable<Book> AllBooks {set;}
        IEnumerable<Book> Search(string searchstring);
    }
    
    public class MyOwnLibrary
    {
        ILibrary Lib=null;
        IEnumerable<Book> FoundBooks=null;
        
        [DSL]
        void i_have_the_following_books(int[] nr,string[] title,string[] author,string[] keywords)
        {
            IList<Book> l = new List<Book>();
            for (int i=0;i<nr.Length;i++)
            {
                l.Add(new Book() { 
                          Id=nr[i],
                          Title = title[i],
                          Author = author[i],
                          Keywords = keywords[i].Split(',')
                      });
            }
            Lib.AllBooks = l;
        }
        
        [DSL("I search for \"(?<what>.+)\"")]
        void Search(string what)
        {
            FoundBooks = Lib.Search(what).ToArray();
        }
        
        [DSL("it should (?<not>not )?(return|show) (?<amount>\\d+) book(s?)")]
        bool Returnamount(string not,int amount)
        {
            return (FoundBooks.Count() == amount) ^ !string.IsNullOrEmpty(not);
        }
        
        [DSL("it should (?<not>not )?(return|show) a book with the title \"(?<title>.+)\"")]
        bool ShouldReturnBook(string not,string title)
        {
            return FoundBooks.Any(x=>x.Title == title)  ^ !string.IsNullOrEmpty(not);
        }

        [DSL("it should (?<not>not )?(return|show) book nr (?<nr>\\d+)")]
        bool ShouldReturnBook(string not,int nr)
        {
            return FoundBooks.Any(x=>x.Id == nr) ^ !string.IsNullOrEmpty(not);
        }
   }
}

Once finished, extract the book class, and move it to the main project (MyFirstBDD), under the Model namespace. After doing this, you also extract and move the ILibrary interface to it's proper location (i.e. project MyFirstBDD, namespace Service)

Unit testing 

Once you have done this, we need to setup unit testing; this is currently some kind of a hack, and it works as follows:

Add a class named Fixture to your test project and use the following code:

using System;
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;

namespace MyFirstBDD.Tests
{
    [TestFixture]
    public class Fixture : Aubergine.NUnitBDD.Fixture 
    {
        public override IEnumerable<string> SpecFiles
        {
            get {
                yield return Path.Combine(Directory.GetCurrentDirectory(),"specs.txt");
            }
        }
        
        public Fixture()
        {
        }
    }
}

Also make sure you change the properties for the file specs.txt to copy "Always" and build action "Content". Also add a reference to the needed libs (both Aubergine and project references)

Change your project type for MyFirstBDD from "console project" to "Class library" in the properties and everything should compile !!

Right click on the fixture class definition in the source code, and you should see the context option "Unit test" available

If you do run the tests, it will say no tests are ran yet, but we'll fix that soon...

Implement the application

Up next is the implementation of the application; since this is only an example, I will simply implement the necessary stuff as simple as possible:

using System;
using System.Collections.Generic;
using System.Linq;
using MyFirstBDD.Model;
using System.Text.RegularExpressions;

namespace MyFirstBDD.Service.Impl
{
    public class Library : ILibrary
    {
        public Library()
        {
        }
        
        public IEnumerable<Book> AllBooks { set; protected get;}
        
        public IEnumerable<Book> Search(string searchstring)
        {
            var re = new Regex(@"\s"+Regex
                               .Escape(searchstring)
                               .Replace(Regex.Escape("*"),"(.*)")
                               .Replace(Regex.Escape("?"),".")
                               +@"\s");
            foreach(var b in AllBooks)
            {
                var kw = string.Join(" ",b.Keywords.ToArray());
                var arr = " "+b.Author+" "+kw+" " + b.Title+" ";
                if (re.IsMatch(arr))
                    yield return b;
            }
        }
    }
}

Now setup your test fixture

Make sure your test code now uses this code. In this case we only have a single dependency, and we use it directly. a typical real-life example would have multiple dependencies, and use probably some mocks/stubs instead of the real service, in order to test only the part you are interested in...

    public class MyOwnLibrary
    {
        public MyOwnLibrary()
        {
        }
        
        ILibrary Lib=new Service.Impl.Library();
        IEnumerable<Book> FoundBooks=null;
        
        [DSL]
        void i_have_the_following_books(int[] nr,string[] title,string[] author,string[] keywords)
        {
    ...

And execute your testcode (with coverage percentage if you want too)

Right click on the fixture class definition, unit test, unit test with coverage...

If everything went well, you should see the following (similar) output:

So you can see everything passed, and we have got a whopping 100% code coverage !!! (not hard in this case, but hey Smile | :) )

We can now start on our UI, but that is a whole different story (Weak event messagebroker comes to mind for example)

In Conclusion

While this is by no means a complete guide on BDD development, this should be enough to get you started.

If you like this, or have some remarks, please do let me know !!!

For any other articles on BDD on this blog, you can follow this link: corebvba - BDD

Wait, what about a ready-built solution ?

Here you go:

MyFirstBDD.zip (72,79 kb)

Enjoy ! 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Tom Janssens
Founder Core bvba
Belgium Belgium
Tom Janssens, owner of Core, a software and consultancy company.
Father of two sons named Quinten & Matisse, and married to a beautiful woman named Liesbeth.
 
Blog: http://tojans.me
Github: http://github.com/ToJans
Twitter: http://twitter.com/ToJans
LinkedIn: http://www.linkedin.com/in/tomjanssens

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 12 Aug 2010
Article Copyright 2010 by Tom Janssens
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid