Los Techies : Blogs about software and anything tech!

Introducing NUnit.Behave or insert what ever other catchy name


OK I was bored yesterday and I decided to update what I had worked on the other day from Dan North's post on rbehave.

Let look at my original attempt.

        [Test]
        public void Transfer_to_cash_account()
        {
            /*
                 As a savings account holder
                 I want to transfer money from my savings account
                 So that I can get cash easily from an ATM)
             */

            Account savings = null;
            Account cash = null;

            Scenario("savings account is in credit");
            Given("my savings account balance is", 100,
                  delegate(int accountBallance)
                      {
                          savings = new Account(accountBallance);
                      });

            Given("my cash account balance is", 10,
                  delegate(int accountBallance)
                      {
                          cash = new Account(accountBallance);
                      });

            When("I transfer to cash account", 20,
                 delegate(int transferAmount)
                     {
                         savings.TransferTo(cash, transferAmount);
                     });

            Then("my savings account balance should be", 80,
                 delegate(int expectedBallance)
                     {
                         Assert.AreEqual(expectedBallance, savings.Ballance);
                     });
            Then("my cash account balance should be", 30,
                 delegate(int expectedBallance)
                     {
                         Assert.AreEqual(expectedBallance, cash.Ballance);
                     });

        }

This will work but the delegates can get really old especially is you want to have several scenarios.  So I decided to use basially make a composite key out of the behavior and the message.  I then created a Hashtable to store the key with the corresponding delegate.  What does this allow me to do.  Well now I can write additional scenarios with out having to worry about the initial plumbing.  Observe.

        [Test]
        public void Transfer_to_cash_account()
        {
            /*
                 As a savings account holder
                 I want to transfer money from my savings account
                 So that I can get cash easily from an ATM)
             */

            Account savings = null;
            Account cash = null;

            Scenario("savings account is in credit");
            Given("my savings account balance is", 100,
                  delegate(int accountBallance)
                      {
                          savings = new Account(accountBallance);
                      });

            Given("my cash account balance is", 10,
                  delegate(int accountBallance)
                      {
                          cash = new Account(accountBallance);
                      });

            When("I transfer to cash account", 20,
                 delegate(int transferAmount)
                     {
                         savings.TransferTo(cash, transferAmount);
                     });

            Then("my savings account balance should be", 80,
                 delegate(int expectedBallance)
                     {
                         Assert.AreEqual(expectedBallance, savings.Ballance);
                     });
            Then("my cash account balance should be", 30,
                 delegate(int expectedBallance)
                     {
                         Assert.AreEqual(expectedBallance, cash.Ballance);
                     });
            
            Given("my savings account balance is", 400);
            Given("my cash account balance is", 100);
            When("I transfer to cash account", 100);
            Then("my savings account balance should be", 300);
            Then("my cash account balance should be", 200);

        }

Much nicer but then I didn't like how it read so I created a template pattern that would allow the Given, When, Then methods to return a conjunction type that would allow me to write more readable scenarios. For instance:

            Given("my savings account balance is", 500)
                .And("my cash account balance is", 20)
                .When("I transfer to cash account", 30)
                .Then("my savings account balance should be", 470)
                .And("my cash account balance should be", 50);


            Scenario("savings account is overdrawn");
            Given("my savings account balance is", -20)
                .And("my cash account balance is", 10)
                .When("I transfer to cash account", 20)
                .Then("my savings account balance should be", -20)
                .And("my cash account balance should be", 10);

Yup that's the ticket.  Now you can run the test from within NUnit and the test runner will produce the following output in the console.

-----------------------------------------------------

Scenario: savings account is in credit
Given my savings account balance is: 100
And my cash account balance is: 10
When I transfer to cash account: 20
Then my savings account balance should be: 80
And my cash account balance should be: 30

Scenario: savings account is in credit
Given my savings account balance is: 400
And my cash account balance is: 100
When I transfer to cash account: 100
Then my savings account balance should be: 300
And my cash account balance should be: 200

Scenario: savings account is in credit
Given my savings account balance is: 500
And my cash account balance is: 20
When I transfer to cash account: 30
Then my savings account balance should be: 470
And my cash account balance should be: 50

Scenario: savings account is overdrawn
Given my savings account balance is: -20
And my cash account balance is: 10
When I transfer to cash account: 20
Then my savings account balance should be: -20
And my cash account balance should be: 10
-----------------------------------------------------

 

As you can see the message heap evaluates for redundant actions ("Given" "Given") and replaces the second instance with "And" making the output much more readable.

Here is the entire test fixture.

using NUnit.Behave;
using NUnit.Framework;

namespace BehavioralExample
{
    public class Account_Specs : BehavioralFixture
    {
        [Test]
        public void Transfer_to_cash_account()
        {
            /*
                 As a savings account holder
                 I want to transfer money from my savings account
                 So that I can get cash easily from an ATM)
             */

            Account savings = null;
            Account cash = null;

            Scenario("savings account is in credit");
            Given("my savings account balance is", 100,
                  delegate(int accountBallance)
                      {
                          savings = new Account(accountBallance);
                      });

            Given("my cash account balance is", 10,
                  delegate(int accountBallance)
                      {
                          cash = new Account(accountBallance);
                      });

            When("I transfer to cash account", 20,
                 delegate(int transferAmount)
                     {
                         savings.TransferTo(cash, transferAmount);
                     });

            Then("my savings account balance should be", 80,
                 delegate(int expectedBallance)
                     {
                         Assert.AreEqual(expectedBallance, savings.Ballance);
                     });
            Then("my cash account balance should be", 30,
                 delegate(int expectedBallance)
                     {
                         Assert.AreEqual(expectedBallance, cash.Ballance);
                     });

            Given("my savings account balance is", 400);
            Given("my cash account balance is", 100);
            When("I transfer to cash account", 100);
            Then("my savings account balance should be", 300);
            Then("my cash account balance should be", 200);

            Given("my savings account balance is", 500)
                .And("my cash account balance is", 20)
                .When("I transfer to cash account", 30)
                .Then("my savings account balance should be", 470)
                .And("my cash account balance should be", 50);


            Scenario("savings account is overdrawn");
            Given("my savings account balance is", -20)
                .And("my cash account balance is", 10)
                .When("I transfer to cash account", 20)
                .Then("my savings account balance should be", -20)
                .And("my cash account balance should be", 10);
        }
    }
}

public class Account
{
    private int accountBallance;

    public Account(int accountBallance)
    {
        this.accountBallance = accountBallance;
    }

    public int Ballance
    {
        get { return accountBallance; }
        set { accountBallance = value; }
    }

    public void TransferTo(Account account, int amount)
    {
        if (accountBallance > 0)
        {
            account.Ballance = account.Ballance + amount;
            Ballance = Ballance - amount;
        }
    }
}

I am uploading the code to Google code and should have it ready shortly.  Just having trouble with Ankh and Tortoise. 

I am not to crazy about inheriting an abstract class as Scott Bellware mentioned but I just can't seem to get around it since C# is not a dynamic language.  Let me know what you all think?  Am I on the right track?  Either way I am having fun coding!

You can download the code from the following link:

http://code.google.com/p/nunitbehave/source

Kick It on DotNetKicks.com
Posted Jun 28 2007, 09:36 PM by Joe Ocampo

Comments

DotNetKicks.com wrote Introducing NUnit.Behave or insert what ever other catchy name
on 06-28-2007 10:18 PM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

joeyDotNet wrote re: Introducing NUnit.Behave or insert what ever other catchy name
on 06-29-2007 10:51 AM
Very cool syntax indeed. I just can't get enough of fluent interfaces... I have NO experience in extending any of the xUnit frameworks, but for what it's worth, I've heard that MbUnit is a bit more extensible, but again, I totally can't back that up. Just maybe something to look into... I'm a huge fan of using MbUnit... it's like NUnit on steriods... :)
Joe Ocampo wrote re: Introducing NUnit.Behave or insert what ever other catchy name
on 06-29-2007 11:51 AM

Joey

I haven't looked into MbUnit since I have been using NUnit for some time.  Not that I am thrilled with NUnit but it get the job done.

I don't think that it would be too hard to extended what I have done for NUnit to work with any other testing framework it's just a matter of wrapping my head around the TestFixture and assertion model.  But doable none the less.

I can't agree more on fluent interfaces.

Steven Solomon wrote re: Introducing NUnit.Behave or insert what ever other catchy name
on 07-05-2007 7:19 AM
I was just wondering why you created your own action delegate rather that use the .Net framework Action delegate? I change the code to use this, and it all works fine.
Joe Ocampo wrote re: Introducing NUnit.Behave or insert what ever other catchy name
on 07-05-2007 7:32 AM

Steven,

I plead ignorance.  Thanks for the update, I will update the code.

Garth wrote re: Introducing NUnit.Behave or insert what ever other catchy name
on 07-11-2007 2:18 AM

Maybe this could be combined with NSpec (http://nspec.tigris.org/) so you can drop the [Test] attribute and Asserts?

my view points wrote Good bye NUnit.Behave hello Behave#
on 07-27-2007 4:16 PM

Most of you who read my blog know that I have been working on NUnit.Behave which was meant to mirror

AgileJoe wrote Good bye NUnit.Behave hello Behave#
on 07-27-2007 4:17 PM

Most of you who read my blog know that I have been working on NUnit.Behave which was meant to mirror

my view points wrote Behave# merges with NBehave
on 09-03-2007 9:08 PM

This is really exciting news! I am proud to say that Behave# and Morgan Persson's NBehave project have

AgileJoe wrote Behave# merges with NBehave
on 09-03-2007 9:14 PM

This is really exciting news! I am proud to say that Behave# and Morgan Persson's NBehave project

ISerializable - Roy Osherove's Blog wrote The evolution of unit testing and syntax
on 01-17-2008 5:22 AM

The semantics of how you write a unit test, the basic syntax, or Domain Specific language of how we write

GrabBag wrote Some Behave# news
on 03-27-2008 4:58 PM

This post was originally published here . Joe already announced this , but it doesn't hurt to get

The evolution of Unit Testing Syntax and Semantics | Developer Home wrote The evolution of Unit Testing Syntax and Semantics | Developer Home
on 05-22-2008 5:33 PM

Pingback from  The evolution of Unit Testing Syntax and Semantics | Developer Home

Robert Durgin » Blog Archive » NUnit.Behave wrote Robert Durgin » Blog Archive » NUnit.Behave
on 10-25-2008 8:02 AM

Pingback from  Robert Durgin  » Blog Archive   » NUnit.Behave

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems