RBehave with NUnit

I just read Dan North’s article on Introducing RBehave.

In a jealous rage of not being able to utilize Ruby fully in .Net. (YET)  I decided to see if I could pull off what Dan had created in rbehave using NUnit.  Because I am a huge proponent of reuse, I didn’t want to extend my existing Test Coverage with a brand new framework.  After all BDD coupled with ReSharper and Visual Studio make for happy testing. 

Now before I have the entire TDD community up in arms with what I am about to propose this is merely a pattern for the porting of rbehave to NUnit without having to invest in a whole new framework.

I would like to reiterate Dan’s vision of what rbehave is intended to be.

“rbehave is a framework for defining and executing application requirements. Using the vocabulary of behaviour-driven development, you define a feature in terms of a Story with Scenarios that describe how the feature behaves. Using a minimum of syntax (a few “quotes” mostly), this becomes an executable and self-describing requirements document.”

Now here is Ruby code for using rbehave:

require ‘rubygems’
require ‘rbehave’
require ’spec’ # for “should” method

require ‘account’ # the actual application code

Story “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) do

  Scenario “savings account is in credit” do
    Given “my savings account balance is”, 100 do |balance|
      @savings_account = Account.new(balance)
    end
    Given “my cash account balance is”, 10 do |balance|
      @cash_account = Account.new(balance)
    end
    When “I transfer”, 20 do |amount|
      @savings_account.transfer_to(@cash_account, amount)
    end
    Then “my savings account balance should be”, 80 do |expected_amount|
      @savings_account.balance.should == expected_amount
    end
    Then “my cash account balance should be”, 30 do |expected_amount|
      @cash_account.balance.should == expected_amount
    end
  end

  Scenario “savings account is overdrawn” do
    Given “my savings account balance is”, -20
    Given “my cash account balance is”, 10
    When “I transfer”, 20
    Then “my savings account balance should be”, -20
    Then “my cash account balance should be”, 10
  end
end

 So I needed a way to capture the story concept using NUnit.  I figured why not use the class text fixture attribute.

NUnit Syntax
    public class Transfer_to_cash_account : NBehaveAbstractFixture
    {
        /*
         As a savings account holder
         I want to transfer money from my savings account
         So that I can get cash easily from an ATM)
         */

}

RBehave Syntax Story “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) do

As you can see the story “transfer to cash account” is the name of the public class Transfer_to_cash_account.  As I am sure you all noticed the [TestFixture] attribute is missing but you will also notice that the Transfer_to_cash_account class inherits from NBehaveAbstractFixture, more on this later.  Lets talk about the real meat here, the “Given” “When” “Then” constructs.

NUnit
Syntax
        [Test]
        public void savings_account_is_in_credit()
        {
            Account savings = null;
            Account cash = null;

            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", 20,
                delegate(int transferAmount)
                {
                    savings.TransferTo(cash, transferAmount);
                });

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

        }

RBehave Syntax   Scenario “savings account is in credit” do
    Given “my savings account balance is”, 100 do |balance|
      @savings_account = Account.new(balance)
    end
    Given “my cash account balance is”, 10 do |balance|
      @cash_account = Account.new(balance)
    end
    When “I transfer”, 20 do |amount|
      @savings_account.transfer_to(@cash_account, amount)
    end
    Then “my savings account balance should be”, 80 do |expected_amount|
      @savings_account.balance.should == expected_amount
    end
    Then “my cash account balance should be”, 30 do |expected_amount|
      @cash_account.balance.should == expected_amount
    end
  end

I have intentionally left the account assertion at 100 so it will generate the following stack trace.

System.ArgumentException: 
-Behavior Heap-
Given my savings account balance is: 100
Given my cash account balance is: 10
When I transfer: 20
Then my savings account balance should be: 100

at AlamoCoders.BDD.Domain.Specs.NBehaveAbstractFixture.Then[T](String message, T actionValue, action`1 delegateAction) in NBehaveAbstractFixture.cs:line 59
at AlamoCoders.BDD.Domain.Account_Specs.Transfer_to_cash_account.savings_account_is_in_credit() in RBehave.cs:line 40

NUnit.Framework.AssertionException:   Expected: 100
  But was:  80

The magic is in the anonymous generic delegate.  Rather than tell you how this works let me show you.  Here is the code for the the NBehaveAbstractFixture.

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

namespace AlamoCoders.BDD.Domain.Specs
{
    [TestFixture]
    public class NBehaveAbstractFixture
    {
        private string messageHeap;

        protected delegate void action<T>(T value);

        [SetUp]
        protected virtual void SetUp()
        {
            messageHeap = "rn-Behavior Heap-rn";
        }

        [TearDown]
        protected virtual void TearDown()
        {
            messageHeap = string.Empty;
        }

        protected void When<T>(string message, T actionValue, action<T> delegateAction)
        {
            try
            {
                InvokeDelegateAction("When", actionValue, delegateAction, message);
            }
            catch (Exception e)
            {
                throw BehaviorException("When", actionValue, e, message);
            }
        }

        protected void Given<T>(string message, T actionValue, action<T> delegateAction)
        {
            try
            {
                InvokeDelegateAction("Given", actionValue, delegateAction, message);
            }
            catch (Exception e)
            {
                throw BehaviorException("Given", actionValue, e, message);
            }
        }

        protected void Then<T>(string message, T actionValue, action<T> delegateAction)
        {
            try
            {
                InvokeDelegateAction("Then", actionValue, delegateAction, message);
            }
            catch (Exception e)
            {
                throw BehaviorException("Then", actionValue, e, message);
            }
        }

        private void InvokeDelegateAction<T>(string methodBehavior, T actionValue, action<T> delegateAction, string message)
        {
            delegateAction(actionValue);
            AddMessageToMessageHeap(actionValue, message, methodBehavior);
        }

        private ArgumentException BehaviorException<T>(string methodBehavior, T actionValue, Exception e, string message)
        {
            AddMessageToMessageHeap(actionValue, message, methodBehavior);
            return new ArgumentException(messageHeap, e);
        }

        private void AddMessageToMessageHeap<T>(T actionValue, string message, string methodBehavior)
        {
            messageHeap += string.Format("{0} {1}: {2}rn", methodBehavior, message, actionValue);
        }
    }
}

As you can see this is very crude but it works!  Let me know what you think.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Joe Ocampo

My personal philosophy is simple: "Have a good strategy that sets the environment for success through the enablement of the whole. Be agile but with a mind towards pragmatism. Delegate to the best qualified individuals, but don’t be afraid to involve yourself in all parts of a job. Treat everyone with respect, humility, and with a genuine pursuit towards excellence." Respected business and technical leader with expertise in directing organization towards effective results driven outcomes. Proven ability to perform and communicate from both technical and business perspectives. Strong technical and business acumen developed through experience, education and training. Provides the ability to utilize technology, harness business intelligence and execute strategically by optimizing systems, tools and process. Passionate about building people, companies and software by containing cost, maximizing operational throughput and capitalize on revenue. Looks to leverage the strengths of individuals and grow the organization to their maximum potential by harnessing the power of their collective whole and deliver results. Co-Founder of LosTechies.com
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

Comments are closed.