Big picture thoughts on software and other topics

November 2, 2007

BDD with just NUnit - a First Attempt

by Brian Donahue

One of the most lively and interesting sessions at the ALT.NET Conference was on Behavior Driven Development.  BDD is often described as an evolution of Test Driven Development.  Proponents of BDD felt that TDD focused too much on testing code, and not enough on testing behavior. 

Doesn’t that just clear it right up?  Not so much?  Let’s try again.  With TDD it’s easy to become focused on testing your objects and methods and making sure they are doing what you think they are supposed to.  But in doing so, it’s easy to lose focus on the higher level user story that you are driving out.  BDD focuses on tying your tests back to a user story and/or an accompanying set of acceptance criteria.  It not only helps to ensure that you are only writing code that is necessary to achieve your customer’s goals but when done well, you can actually use your tests to help show your customer that a story is complete.

Some people look at BDD and think that it is just TDD with longer, stranger names for the test methods:

"Classic" TDD Naming Example:

[TestFixture]
public class OrderHistoryWebServiceTest
{
[Test]
public void GetOrderHistory()
{
// Try to connect and retrieve order history for a user
}
}

BDD Style Naming Example:

[TestFixture]
public class When_user_views_order_history
{
[Test]
public void Should_retrieve_all_completed_order_data_for_user_from_web_service()
    {
// Try to connect and retrieve order history for a user
}
}

So the test code itself would look the same, right?  Maybe, maybe not.  The real value of BDD can be elusive, but it really has a lot to do with the names, and writing your tests in a way that is driven by your user stories and acceptance criteria.  Doing this can really help to ensure adherence to many of the principles that (I hope) you already hold dear:

I am pretty new to BDD, and even though I try to practice to TDD regularly, I have by no means mastered that either!  But I do see BDD as an approach that can help me hone my TDD skills.  One of my biggest challenges when I code test-first is that I tend to over-think everything.  I don’t do the simplest thing that could work.  I start to architect the application in my head, think about all the components and how the test I am writing fits in to the whole.  This causes me to stop and restart repeatedly as I think "oh, well that works for this case, but then later I’ll need to do this, and that doesn’t look pretty enough in that scenario..."  It’s a terrible habit.  The mantra is "Red, Green, REFACTOR" for a reason.

Approaching your tests from a BDD mindset, though, helps to keep you focused on the particular story you are driving out.  I find myself worrying less (I’m not cured, yet) about outlying scenarios, and focusing more on the specific behavior I’m driving out. 

Given the following user story:
As a shopper, I want to be able to review my entire order history so that I can easily re-order items that I’ve ordered in the past.

You might drive out these acceptance criteria with your customer:

This story could probably be broken down better (there is possibly more than one story here) but let’s stick with these parameters for now.  In traditional TDD, I might start thinking about the data involved here, and start to write tests for the service that would retrieve this data first, then start to deal with the presentation/view layer and how it might display it, etc.  For me, it would be easy to get sidetracked thinking what else my OrderService class might need to do, or possibly how an order’s details will be retrieved, etc, when that is not really part of this story. 

In BDD, I would start by focusing on a behavior, and a context.  For example, the "happy days" scenario of a user with prior orders that is authenticated and wants to view her past orders.  I’d probably start with a test like this:

[TestFixture]
public class When_a_user_has_orders_and_views_order_history // this describes the context
{
[Test]
public
void Should_display_10_most_recent_orders_in_users_history()
{
// This code is for example only!
IOrderService mockOrderService = mockRepository.CreateMock<IOrderService>();
IOrderHistoryView mockView = mockRepository.CreateMock<IOrderHistoryView >();
IEnumerable<OrderHistoryDTO> orderData = GetFakedOrderDTOs(10); // we’re not testing the service yet!
int userId = 1000;
int pageNumber = 1;

using(mockRepository.Record())
{
Expect.Call(mockOrderService.GetOrderHistory(userId, pageNumber).Return(orderData);
mockView.BindOrderList(orderData); // expectation for void method
}

using(mockRepository.Playback())
{
OrderHistoryPresenter presenter = new OrderHistoryPresenter(mockView, mockOrderService);
presenter.GetOrderHistory(userId, pageNumber);
}
}
}

Now, if you already do TDD (and Model-View-Presenter as in this example) you might say "big deal, I already do it this way, without the ridonkulously long test names!".  And you’d probably be right.  But in my experience, instead of this context focused test, I’d end up with one big OrderHistoryPresenterTest in which I’d try to test everything that would ever be asked of the OrderHistoryPresenter and I’d probably get stuck a bunch of times wondering if X or Y function belongs on this presenter, or somewhere else.  Then I’d move on to the OrderService and hit the same problems.  With BDD, I can focus on one behavior, and drive it all the way out down through my entire application stack.  It also jives with the top-down design approach that people like JP Boodhoo have been promotingBroken Link: http://sharepoint.codebetter.com/blogs/jean-paul_boodhoo/archive/2007/09/27/screen-bound-dto-s.aspx

One thing I am struggling with is consistent, meaningful naming of tests as I get deeper into my application layers.  For example, for the story above, I found myself naming the next TestFixture "When_the_order_service_gets_order_history_for_a_user"  and maybe on to "When_the_order_gateway_retrieves_orders_from_the_database" or some such.  It seems to get very redundant and kind of silly pretty fast. Also, one of the goals (or at least a side effect) of BDD is that you should be able to generate documentation from your tests that read very much like specifications.  Frameworks like RSpec and NBehave can help with this.  But a simple code parser that strips out underscores for the above could generate a customer-friendly report:
When a user has orders and views order history

But if you are writing a new test for each layer, it would seem to get confusing for a user:

When the order service retrieves orders for a users order history

That report in itself isn’t that bad, but as your application grows, and there are multiple layers, I can imagine a customer getting pretty flustered and saying "I just want to know that it does what I want, not HOW it does what I want!"

So I am pretty sure I am missing a lot of the subtlety of practicing BDD, and look forward to more posts on it by people like Scott BellwareBroken Link: http://codebetter.com/blogs/scott.bellware/ and Joe OcampoBroken Link: file:///Users/brian/development/persistall/websites/persistall.com-2010/archive/2007/11/02/www.lostechies.com/blogs/joe_ocampo who were two of the leaders of the session at the ALT.NET Conference.  Joe works on NBehaveBroken Link: http://nbehave.org/, a framework on top of NUnit which tries to bring the language of your tests closer to the language of your business users so that they can easily read, and potentially even write, tests for the application.  Scott uses RSpecBroken Link: http://rspec.rubyforge.org/ which he finds much more suitable to this task.  From what I’ve seen, I don’t find the NBehave syntax very clear at this stage, and while RSpec definitely reads better (doesn’t practically everything in Ruby read better?), I need to understand better how it is used to really comprehend its value.  There is also muchBroken Link: http://codebetter.com/blogs/scott.bellware/archive/2007/10/14/169723.aspx debate on whether BDD has anything to do with "executable" or "traceable" requirementsBroken Link: http://codebetter.com/blogs/scott.bellware/archive/2007/10/28/170346.aspx, or if it is simply a software design tool that can be used to help bridge the gap between developers and business usersBroken Link: http://codebetter.com/blogs/scott.bellware/archive/2007/10/19/169985.aspx, but is not meant to serve as a fixed artifact generated from that interaction.

Rest assured there will be a lot of movement in the BDD arena over the next months and years, and hopefully with some of the dynamic languages (especially IronRuby!) coming to life on the .NET framework maybe we’ll soon be able to use a BDD framework that aligns more closely with the way we think about building and testing our applications. 





Technorati Tags: bddBroken Link: http://technorati.com/tag/bdd, tddBroken Link: http://technorati.com/tag/tdd, nunitBroken Link: http://technorati.com/tag/nunit, asp.netBroken Link: http://technorati.com/tag/asp.net, altnetconfBroken Link: http://technorati.com/tag/altnetconf