More BDD xBehave Madness!
Well over the past week or so I have been working off and on with improving the usage of NUnit.Behave. It started off being tightly coupled with NUnit since you actually had to inherit an abstract fixture that exposed the “Given, When, Then” BDD constructs. With a bit of tinkering with C# generics and some fluent interface magic, I managed to decouple the code into its own, dare I say, framework.
Before I continue I need you to understand the goal of what I am creating. Based on Dan North’s initial vision of rbehave, I modeled the syntax of the xBehave (insert catchy agile term here) framework around rbehave. The primary goal of rbehave is a framework for defining and executing application requirements. These definitions are modeled after BDD terms such as Story, Scenario, Given When Then. Using a minimum of syntax (a few “quotes” mostly), this becomes an executable and self-describing requirements document. By utilizing the definitions within the actual unit test code of the application, the ubiquitous nature of the architecture and domain become one cohesive amalgam. With the help of Domain Driven Design all these concepts seemingly bring together our software. The code actually becomes what we have always wanted, living requirements that are constantly asserted on to insure their viability and accuracy from the inception of the software architecture. Can you say true tracebility!
Now lets consider something for a moment. Who are the owners of our requirements(stories)? If you said the product owner, then you guessed correctly. So we have a product owner who wants to tell us how they think our software should behave. So lets take this a little further.
In my shop, we practice agile development. Our release cycles are comprised of a storming phase, planning phase, development phase, SIT/UAT phase and finally production deployment. Each release cycle is typically 6 weeks long. During the storming and planning phase, we conduct modeling sessions comprised of a product owner, modeler, developer and systems analyst (QA). Utilizing a Domain Driven approach we model each story to understand the business need and value. The problem is that various artifacts have to be maintained during these sessions. Namely wire frames, domain models, and test plans. Not to mention the actual story cards. I am not going to lie to you and tell you that everything runs like a well oiled machine. After all we do have people in the equation here. Their in lies the problem, people think in different ways. Trying to enforce certain story context structures As, I want, So that, seems to be hit and miss. These concepts are hard to enforce when the business is your customer. So my goal was to try and see if a product owner could actually code the requirements in Visual Studio and hope that my type constraints would be enforced at design time.
I approached one of our product owners and asked her if she would be willing to try an experiment with me. She was very reluctant at first but when I explained that I may have an idea that can save her time from a story authoring standpoint and introduce quality from inception she quickly became interested. Like most product owners she had never utilized an IDE. I opened up Visual Studio and created a basic NUnit test fixture. I opened up Dan North’s blog on another monitor and showed her the basic story for transfer account.
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
I told her we are going to create a new story based on this model but I want you to type it in code. She quickly told me that she had never typed any code in her life and she doesn’t know how productive she could be. So I walked her though the instantiation of the story as I knew this would probably be the hardest concept to grasp.
Story story = new Story(“Transfer to cash account”);
I explained to her that the title of the story is the first piece of information that is needed. I showed her the intellisence that visual studio would lend to help her along.
After that we went ahead and created the remainder of the story using a fluent interface. I made certain not the expose all the fluent behaviors off the story class. I did this because I wanted to walk the user through the template. If I exposed all the behaviors at once they could mess up the fluidity of the interface. Such as:
story.I_want(“to transfer money from my savings account”)
.As_a(“savings account holder”)
.So_that(“I can get cash easily from an ATM”);
This doesn’t help the user, so after they press “.” on the story class the only methods that are exposed are:
I did this by chaining together conjunction types that expose only the required methods at one given point in time. Works out quiet well since the editor enforces the type hierarchy at design time. Notice how similar it is to rbehave (lots of quotes).
In the end we ended up with the following syntax.
[Test]public void Transfer_to_cash_account()
{
Story transfer_to_cash_account = new Story(“Transfer to cash account”); 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”); Scenario savings_account_is_in_credit = new Scenario(“savings account is in credit”); 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); 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);
}
When you run the test runner you receive the following output.
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 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</font> 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</font>
The
I asked her if she thought if she could do this with ALL the stories her team creates. I proposed that during the storming phase her team(business analyst) just create the stories. During the planning phase we can come up with the scenarios and edit them together. From there the developers can take these behavioral scenarios and implement the code to insure that we created the functionality according to business teams specifications. We both agreed that it was possible but that it would take time to get everyone on board.
I asked her 3 closing questions.
- Was the story easy to author? Yes “After I got use to the little boxes popping up and remembering to use quotes.”
- Were the scenarios east to author? Yes, “but the action value took me a while to get use too.”
- Is the output valuable? Not really, it helps but I don’t see how we would use it.
I followed up question 3 with ” I am working on a way to output all the output to an HTML page similar to CruiseControl and you can visually see after every build what stories have been completed and what stories haven’t. She seemed OK with the response but reserved on actually seeing the final product.
So as a developers we would simple use the Action
public void Transfer_to_cash_account()
{
Story transfer_to_cash_account = new Story(“Transfer to cash account”); 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 = new Scenario(“savings account is in credit”);
savings_account_is_in_credit
.Given(“my savings account balance is”, 100,
delegate(int accountBallance)
{
savings = new Account(accountBallance);
})
.And(“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);
})
.And(“my cash account balance should be”, 30,
delegate(int expectedBallance)
{
Assert.AreEqual(expectedBallance, cash.Ballance);
}); 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);
}
When you run this test with the delegates wired up you receive the following output:
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 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
As you can tell the
The magic of generics and anonymous generic delegates
I learned a great deal about generics implementing this framework, so I thought I would pass some tidbits of information on to the rest of you.
lets look at the following method:
public GivenContext Given(string theGivenSentence, T actionValue)</font>
Since
.Given(“my savings account balance is”, 100)</font>
Imagine training a business analyst to make sure they use the correct types in the angle brackets. Ummmm No.
But the magic doesn’t stop there. You can use the generic type within an anonymous delegate. Consider the following Given method:
public GivenContext Given(string theGivenSentence, T actionValue, Action delegateAction)</font>
You will notice the Action
Notice that the actionValue being passed in is an integer and in the delegate the developer accidentally thought it should be a string. This type checking at design time Forces the developer to implement the correct parameter types within their code.
I emailed Dan to make certain I was on the correct path with his intentions of rbehave as I created this framework. My hope is that it will gain momentum and help to synergies development around the story aspect of agile development. Bringing the product owners ever closer to the foundation of code can only be a good thing.
As always the latest code can be downloaded from here.