<< Spreading Passion / Enacting Change | Home | Converting an OS X Bootcamp Partition to a VMWare Virtual Machine >>

BDD with just NUnit - a First Attempt

posted @ Friday, November 02, 2007 1:27 PM

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:

  • Doing the simplest thing that works
  • Avoiding premature optimization
  • Ensuring that your work is always tied directly to your customer’s goals
  • Preventing analysis paralysis and BDUF

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:

  • Previous orders should be displayed in descending date order - most recent order first.
  • Order list should show invoice number, date, and total price for each order
  • Order list should be paginated, with 10 orders per page
  • Clicking an order’s invoice number in the list will take the user to the order’s detail page

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 promoting

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

  • Should display most recent orders in users 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

  • Should request order history from the database gateway
  • Should return first 10 orders for first page of data

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 Bellware and Joe Ocampo who were two of the leaders of the session at the ALT.NET Conference.  Joe works on NBehave, 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 RSpec 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 much debate on whether BDD has anything to do with "executable" or "traceable" requirements, or if it is simply a software design tool that can be used to help bridge the gap between developers and business users, 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. 



kick it on DotNetKicks.com

Technorati Tags: , , , ,

Comments

  1. Max Pool

    Posted on: 11/3/2007 12:43 PM

    # re: BDD with just NUnit - a First Attempt

    BDD is a technique for those who need a vehicle to correlate code and user stories, and to help with the discipline of thinking in behaviors and not OO. For that I think it is a great step in the correct direction...

    Personally, I find it (as you say) ridonkulously long test names which I would find as much a nightmare maintaining as a traditional Waterfall requirements document.

    Neither a lover or hater of BDD, just not my style...

  2. Scott Bellware

    Posted on: 11/3/2007 12:48 PM

    # re: BDD with just NUnit - a First Attempt

    In RSpec, you know the context and the class name of the thing under test, so there's often a hint as to which part of the architecture the test is interested in.

    That information often means nothing to the customer, but the spec report is something that the customer should go over with a programmer. It's not meant to be a purely business-oriented document. If it was a purely business-oriented document, it would likely reduce the number of opportunities for a customer and a programmer to have a conversation about the meaning of the system.

  3. Brad Mead

    Posted on: 11/3/2007 1:32 PM

    # re: BDD with just NUnit - a First Attempt

    Excellent post. Exactly the kind of clarifying, concept-level stuff I am always looking for.

  4. Brian Donahue

    Posted on: 11/3/2007 2:01 PM

    # re: BDD with just NUnit - a First Attempt

    @Max - so far the only names that have bothered me a little are the class names, and that was something I tried to get away from the [ObjectUnderTest]Test convention. RSpec handles this better, obviously. But the method names actually make sense to me and look pretty nice in an xUnit report.

    @Scott - I've said it before in comments on your blog, but I would really love to see some BDD examples with code/pseudo-code (through multiple layers of the app) along with a narrative of the thought and customer interaction that go into them. I think this is what you were trying to do at AltNetConf but we got a bit sidetracked!

  5. Chris Patterson

    Posted on: 11/3/2007 2:03 PM

    # re: BDD with just NUnit - a First Attempt

    This is a great read, I really think you've really helped clarify how BDD compares to TDD. In fact, the explanation of context versus behavior is pretty clear in your example.

    I think putting a presentation together on this topic is in your future!

  6. Brian Donahue

    Posted on: 11/3/2007 4:24 PM

    # re: BDD with just NUnit - a First Attempt

    @Max - Update: I just discovered one of the bigger reasons why I don't like the big ugly names for class names. They're no good when browsing types in Resharper using Ctrl-N. Bummer.

  7. Max Pool

    Posted on: 11/3/2007 5:33 PM

    # re: BDD with just NUnit - a First Attempt

    @Brian - Ditto! Going from the method signatures of Method_State_Expectation to the big ugly names is a big bummer when trying to hunt down a particular test case.

  8. Posted on: 11/5/2007 8:47 AM

    # Links from the Sharpside [11.03.07]

    Great post by Brian Donahue that breaks down BDD so that us mortals can get the base concept.

  9. Christian Crowhurst

    Posted on: 11/5/2007 12:14 PM

    # re: BDD with just NUnit - a First Attempt

    Brian,
    How about using BDD-style long names to drive out the code of the Presenter and then use TDD-style naming / OO mindsight for those services that the Presenter uses?

    When in a TDD mindset, let duplication of the fixture in the test method names indicate that you need a new test class named after the duplicate fragment. So for example:

    public class OrderServiceTests{
    public void ShouldReturnOrdersWhenTheUserHasPreviousOrders();
    public void ShouldReturnFirst10OrdersWhenTheUserHasPreviousOrders();
    }

    You would refactor these into a spec/test class named:

    public class OrderServiceTests{

    public class UserHasPreviousOrders{

    public void ShouldReturnOrders();
    public void ShouldReturnFirst10Orders();
    }
    }

  10. Joe Ocampo

    Posted on: 11/5/2007 6:49 PM

    # re: BDD with just NUnit - a First Attempt

    Nice Post.

    >There is also much debate on whether BDD has anything to do with "executable" or "traceable" requirements,

    Pure BDD has nothing to do with this concept this is just a FEATURE of frameworks such as NBehave and RSpec.

    Your post seems to have captured the essence of the "contextual" nature that BDD is trying to provide to traditional TDD.

    At it's heart BDD is simply a mindset approach to TDD that emphasizes context and the assertions against the behavior of that context.

    >But a simple code parser that strips out underscores ...

    Use MbUnit and Run "Dox" command from the Report menu in the GUI runner. You will have to use PascalCasing though in your method names. I am trying to get the MbUnit guys to take into account "_" in version 3.

  11. Brian Donahue

    Posted on: 11/6/2007 7:28 PM

    # re: BDD with just NUnit - a First Attempt

    @Christian - We are on the same wavelength. Was thinking about this, and trying to collect some thoughts into another post, hopefully soon!

    @Joe - Good to know about MbUnit. That is actually what I've been using lately, so I will give that a shot. I went a bit overboard with this naming convention and now kind of want to rename/reshuffle everything :) More on that in the aforementioned upcoming post.

Your comment:



 (will not be displayed)


  Please add 7 and 4 and type the answer here: