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

Dependency Patterns