Click here to Skip to main content
15,884,472 members
Articles / Programming Languages / C# 4.0

Introducing BDDfy: the simplest BDD framework for .Net

Rate me:
Please Sign up or sign in to vote.
4.96/5 (18 votes)
23 Sep 2013CPOL19 min read 51.5K   231   49  
This article is an introduction to installing and using bddify - a simple to use and extend BDD framework for .NET developers.
If you are new to BDD you may want check http://www.mehdi-khalili.com/bdd-to-the-rescue  about BDD. 

bddify is the simplest BDD framework EVER! To use bddify:
 # Install NuGet if you have not already.
 # Go to 'Tools', 'Library Package Manager', and click 'Package Manager Console'.
 # In the console, type 'Install-Package bddify' and enter.

This adds bddify assembly and its dependencies to your test project. Oh, BTW, bddify can work with any and all testing frameworks. In fact, it works even if you are not using any testing framework.

If this is the first time you are using bddify you may want to check out some of the samples on NuGet. Just search NuGet for bddify and you will see a list of bddify samples. You may install one or more samples to see how the framework works. Each sample installs required packages (including bddify, NUnit, RazorEngine and so on).

==Quick start==
Now that you have installed bddify, write your first test (this test is borrowed from ATM sample that you can install using nuget package Bddify.Samples.ATM):

{{{
[Story(
    AsA = "As an Account Holder",
    IWant = "I want to withdraw cash from an ATM",
    SoThat = "So that I can get money when the bank is closed")]
public class AccountHasInsufficientFund
{
    private Card _card;
    private Atm _atm;

    // You can override step text using executable attributes
    [Given(StepText = "Given the account balance is $10")]
    void GivenAccountHasEnoughBalance()
    {
        _card = new Card(true, 10);
    }

    void AndGivenTheCardIsValid()
    {
    }

    void AndGivenTheMachineContainsEnoughMoney()
    {
        _atm = new Atm(100);
    }

    void WhenTheAccountHolderRequests20()
    {
        _atm.RequestMoney(_card, 20);
    }

    void ThenTheAtmShouldNotDispenseAnyMoney()
    {
        Assert.AreEqual(0, _atm.DispenseValue);
    }

    void AndTheAtmShouldSayThereAreInsufficientFunds()
    {
        Assert.AreEqual(DisplayMessage.InsufficientFunds, _atm.Message);
    }

    void AndTheCardShouldBeReturned()
    {
        Assert.IsFalse(_atm.CardIsRetained);
    }

    [Test]
    public void Execute()
    {
        this.Bddify();
    }
}

}}}

And this gives you a report like:
{{{
Story: Account holder withdraws cash
    As an Account Holder
    I want to withdraw cash from an ATM
    So that I can get money when the bank is closed

Scenario: Account has insufficient fund
    Given the account balance is $10
      And the card is valid
    When the account holder requests $20
    Then the atm should not dispense any money
      And the atm should say there are insufficient funds
      And the card should be returned
}}}

This is just the console report. Have a look at your output folder and you should see a nice html report too.

If you want more control you can also use bddify's Fluent API. Here is another example done using the Fluent API:

{{{
[Test]
public void CardHasBeenDisabled()
{
    this.Given(s => s.GivenTheCardIsDisabled())
        .When(s => s.WhenTheAccountHolderRequests(20))
        .Then(s => s.CardIsRetained(true), "Then the ATM should retain the card")
            .And(s => s.AndTheAtmShouldSayTheCardHasBeenRetained())
        .Bddify(htmlReportName: "ATM");
}
}}}

which gives you a report like:
{{{
Scenario: Card has been disabled
    Given the card is disabled
    When the account holder requests 20
    Then the ATM should retain the card
      And the atm should say the card has been retained
}}}

==How does bddify work?==
Bddify uses quite a few conventions to make it frictionless to use. For your convenience, I will try to provide a quick tutorial below:

===Finding steps===
Bddify scans your bddified classes for steps. Currently it has three ways of finding a step: 
 * Using attributes 
 * Using method name conventions 
 * And using fluent API.

Bddify runs your steps in the following order: SetupState, ConsecutiveSetupState, Transition, ConsecutiveTransition, Assertion, ConsecutiveAssertion and TearDown. Some of these steps are reported in the console and html reports and some of them are not. Please read below for further information.

====Attributes====
Bddify looks for a custom attribute called ExecutableAttribute on your method. To make it easier to use, ExecutableAttribute has a few subclasses that you can use: you may apply Given, AndGiven, When, AndWhen, Then, and AndThen attributes on any method you want to make available to bddify.

====Method name convention====
bddify uses some conventions to find methods that should be turned into steps. Here is the current conventions. The method name:

 * ending with "Context" is considered as a setup method (not reported).
 * "Setup" is considered as as setup method  (not reported). 
 * starting with "Given" is considered as a setup method (reported). 
 * starting with "AndGiven" is considered as a setup method that runs after Context, Setup and Given steps (reported).
 * starting with "When" is considered as a transition method  (reported). 
 * starting with "AndWhen" is considered as a transition method that runs after When steps (reported).
 * starting with "Then" is considered as an asserting method (reported).
 * starting with "And" is considered as an asserting method (reported).
 * starting with "TearDown" is considered as a finally method which is run after all the other steps (not reported).

As you see in the above example you can mix and match the executable attributes and method name conventions to acheive great flexibility and power.

====Fluent API====
Fluent API gives you the absolute power over step selection and their titles. When you use Fluent API for a test, the attributes and method name conventions are ignored for that test. 

Pleasee note that you can have some tests using fluent API and some using a combination of attributes and method name conventions. Each .Bddify() test works in isolation of others.

==Other conventions==
Bddify prefers convention over configuration; but it also allows you to configure pretty much all the conventions. Here I will try to list some of the conventions and the way you can override them:

===Method name convention===
As mentioned above, by default bddify uses method name convention to find your steps. You can override this using ExecutableAttribute or Fluent API as discussed above.

===Step title convention===
bddify extracts steps titles differently depending on the way steps are found:

====When using method name convention====

====When using ExecutableAttributes====

====When using Fluent API====

===Scenario title convention===

========

==More info==
If you want to understand how the framework works and learn about it more please refer to the code project article about the framework at http://www.codeproject.com/KB/library/Introducing_bddify.aspx

==News==
===2011-09-19: Bddify V0.8 release===
This release is about making reports very extensible. Now the reports are published over TraceSource which means you can very simply plug-in your listener and create whatever report suits you. For example, you could write a SQL trace listener that saves the stories and scenarios and their results into database which you can later use! There are two trace sources: one for more instant report; e.g. console report, and one for IO bound reports; e.g. html report. Bddify currently comes with two listeners out of the box for console and html reports. If you do not include a trace listener in the config file bddify falls back to the default listeners and if you specify a listener, your configuration will override the convention.

Changing the reporting model also allowed me to simplify the Bddify extension method and remove optional parameters for console and html reports.

===2011-09-07: Bddify V0.7 released===
This release includes a few bug fixes, some internal cleanup and some love for html report 
  * Fixed a bug where Method Name scanner would collide with ExecutableAttribute scanner (thanks to Michael Whelan for reporting the issue). In order to fix this bug and to clean up its surrounding classes I made quite a few changes to the way scanners work. If you have extended bddify by creating a new scanner, there is a good chance your scanner will not work; that said, it should be very easy to fix it. Feel free to send me your code and I will help fix it.
  * Bddify used to (and still does for backward compatibility) guess the Story by walking up the stack trace. This obviously breaks in inlined and tail call methods. Some changes are made and a new set of extension methods have been added to allow you to specify the story. So if you have a story you should call {{{ .Bddify<YourStoryTypeWithStoryAttributeOnIt> }}}. If you do not specify the story type argument, bddify still does its best to find the story; but in some cases it will fail to find the story and will leave it out. This does not crash your tests; but the reports will be skewed as Bddify cannot associate your scenario with its story.
  * Removed Bowling sample and cleaned up ATM and TicTacToe samples. The cleaned code reflect the way I am currently using Bddify in a project; i.e. cleaner and more production like.
  * Added an ability to specify a custom html report name. You can now classify your behaviors by providing different html report names which will result in different html reports. To do so, you should pass in the custom report name, without the extension, to bddify extension method:
{{{
[Test]
public void AccountHasInsufficientFund()
{
   new AccountHasInsufficientFund().Bddify<AccountHolderWithdrawsCash>(htmlReportName:"ATM");
}
}}}

===2011-07-03: Bddify V0.6 released. Quite a few improvements in API and usability ===
===2011-06-25: Bddify V0.5.1 released with support for .Net 3.5 (and little support for SL4)===

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Chief Technology Officer Genie solutions
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions