Introduction
In this article, you can download the very first alpha version of Aubergine; a BDD /DSL framework for .NET, initially based on Machine.Specifications
, but later on heavily inspired by Cucumber. It is AFAIK the very first Cucumber like environment in .NET available, and you will see that it is very easy to use. Due to its inspirator (Cucumber, I have decided to use the name Aubergine - I have no idea if they are actually related or not .
Please do note that it is an alpha version, so right now we only have a single testrunner that outputs to the console (i.e., no unit test integration yet). In the article, I do include a postbuild step, which automatically makes my BDD tests run after each build, and displays the output in Notepad, which is fine for me atm.
Anyway, enough with the talkin, Let's Get Busy !!!
Getting Started
First, you need to download the example project; it includes all the binaries needed to do your BDD development. You can find it here:
Once you have the zip file, you can either explore the project, or walk through the following scenario to create your own test.
- Create a new .NET project in Visual Studio/any IDE you use
- Add a reference to Be.Corebvba.Aubergine.DLL (can be found in the zip under the "lib" folder
Creating a Story
Add a class named "BrowserContext
" to your project.
Add a class named "Make_sure_my_website_gets_enough_visibility
", import the Be.Corebvba.Aubergine
namespace
, and derive your class from Story
.<browsercontext>
Then, you can start typing your story; the final file should look like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Be.Corebvba.Aubergine;
namespace Example
{
class Make_sure_my_website_gets_enough_visibility: Story<BrowserContext>
{
As_a website_owner;
I_want to_make_sure_that_I_get_enough_visibility;
So_that I_can_get_enough_traffic;
[Cols("searchengine","search_url","keywords","my_url")]
[Data("google","http://www.google.be/search?q=","core bvba tom janssens","www.corebvba.be")]
[Data("google","http://www.google.be/search?q=","BDD .Net","www.corebvba.be")]
[Data("bing","http://www.bing.com/search?q=","core bvba tom janssens","www.corebvba.be")]
class Search_results_for_keywords_on_searchengine_should_contain_my_url : Scenario
{
Given current_url_is_search_url;
When searching_for_keywords;
Then result_should_contain_my_url;
}
}
}
Define the Context Class
After having created a story with scenarios, we need to define the context for these scenarios.
You add all your domain objects that need to be tested to the "BrowserContext
" class:
In this case, it is quite simple:
public class BrowserContext
{
public string Url { get; set; }
public string Result { get; set; }
private WebClient wc = new WebClient();
}
This should be enough to test, but wait, isn't there something missing?
The Context DSL
Next up, we need to define how to interpret the story. As I already mentioned; this is heavily inspired by Ruby/Cucumber: regular expressions are used to find out how all scenario steps should be matched to a real function. While this sounds complicated, it really isn't; this is the code:
public class BrowserContext
{
public string Url { get; set; }
public string Result { get; set; }
private WebClient wc = new WebClient();
[DSL("current_url_is_(.*)")]
void SetUrl(string url)
{
Url = url;
}
[DSL("searching_for_(.*)")]
void SearchForKeyWords(string keywords)
{
Result = wc.DownloadString(Url + HttpUtility.UrlEncode(keywords));
}
[DSL("result_should_contain_(.*)")]
void ResultShouldContain(string myurl)
{
Result.Contains(myurl).ShouldEqual(true);
}
}
That's all there is to it; you are ready to run your tests now !!!
Running the Specs
Ok, since we do not want to run these tests manually each time, but at every build action, we need to add a postbuildstep.
In Visual Studio, you can do it like this:
- Menu: Project/Properties
- Tab build events
- Put the following text in the post-build-event commandline:
"$(ProjectDir)\lib\Be.Corebvba.Aubergine.ConsoleRunner.exe" "$(TargetPath)" > "$(TargetDir)output.txt"
"$(TargetDir)output.txt"
exit 0
Now build your project, and the tests will be run!! Your default texteditor will start, and it should contain this text:
==STORY================================================================
Make_sure_my_website_gets_enough_visibility => OK
========================================================================
Search_results_for_core bvba tom janssens_on_google_should_contain_www.corebvba.be => OK
Given current_url_is_http://www.google.be/search?q= => OK
When searching_for_core bvba tom janssens => OK
Then result_should_contain_www.corebvba.be => OK
Search_results_for_core bvba tom janssens_on_bing_should_contain_www.corebvba.be => OK
Given current_url_is_http://www.bing.com/search?q= => OK
When searching_for_core bvba tom janssens => OK
Then result_should_contain_www.corebvba.be => OK
Search_results_for_BDD .Net_on_google_should_contain_www.corebvba.be => OK
Given current_url_is_http://www.google.be/search?q= => OK
When searching_for_BDD .Net => OK
Then result_should_contain_www.corebvba.be => OK
So How Does This Work?
It's actually quite easy once you have the hang of it; this is pseudocode:
foreach (var story in AllClassesDerivedFromTheAubergineStoryClass)
foreach(var scenario in AllPossibleScenariosFor(story))
create a new context object
foreach (var possiblesteps in AllSteps (given,when,then)
in story and scenario)
find a regex DSL match for possiblesteps.name in the context,
extract all the regex groups and add them as string parameters
to the function you call the corresponding
step function
If one of these steps should fail, then the test fails and the reason is mentioned in the report.
If you expect a step to fail, then you should add a membervariable
named "<steptype>Exception
", and if a step of that type fails, the step is marked as successful, but the Exceptionvariable
will contain the exception thrown. You can see an example of this in the example zip file.