Understanding Behavior Driven Development

I think I'm on the verge of drinking the Cucumber flavored Kool-Aid, which is odd because I've never done Behavior Driven Development (BDD) before. If you've followed my blogs over the years, you know that I am happy to investigate new trends, but at the same time I'm deeply, deeply suspicious of them. BDD has been one of those "fads" that I've been suspicious of, but I think I've changed my mind. What follows is why.

As some of you know, through my company, multiple associates and myself do bespoke software development, consulting, training, and generally go into companies and fix their issues. Part of the training has been based on my Agile Companies Go POP philosophy.

In this training, I tend not to go into the specifics of Agile methodologies, but instead focus on the "big picture" of making companies more responsive to changing business environments. However, clients routinely ask for fine-grained details about how Scrum works (it's almost always Scrum they ask about). One client has struggled to get developers focusing on customer needs instead of being task-focused and much of the issue can stem from poorly-written user stories. For example, here are two user stories.

Story A:

When someone updates a support ticket, an event must be fired off to RabbitMQ to guarantee it can be later emailed to the customer.

Story B:

As a customer, I want to be notified if my support ticket is updated so I can track the progress.

Before you read further, ask yourself which story is better. They describe the same task, but in significantly different ways.

... time passes ...

OK, you've read both the stories. You see that they're trying to accomplish the same thing. You may even have an opinion about which story is better and now you're waiting for my answer.

In Scrum (and in truth, in many other methodologies), the first story is a disaster. The product owner who writes the story should be an advocate for the customer and they should trust the developers to apply their expertise and create an appropriate technical solution. It's not (always) bad if the product owner is technically minded, but that generally shouldn't be showing up in the tickets. When a the person writing the story injects unnecessary technical requirements, not only are they telling the dev team "your expertise isn't really important", but they're also locking the dev team into a particular point of view which may limit their opportunities to explore appropriate solutions.

The second story is in a "who, what, why" format.

As a
<who>
I want a
<what>
because
<why>

By writing a story in that format, you're focusing explicitly on a customer need. The devs reading the story card now know exactly what the customer needs and why they need it.

When you're in sprint planning and you choose a story to fit the theme of a sprint, you should develop acceptance criteria which, if they are met, says "yes, this story was successfully completed." Part of your "definition of done" should be "all acceptance criteria were met."

So what would be the acceptance criteria of the above task? They might look like this:

  • Customers are always notified when a ticket is opened or closed, regardless of other criteria
  • Customers don't get notified if they update their own ticket
  • Customers receive an email if someone else updates the ticket
  • Unless it's an automated notification (spam protection)
  • The email must contain the date the ticket was opened, the name of the updater, the comment, the status, and a link to the ticket

Those criteria produce measurable results, but have minimal technical detail. In other words, you're still focused on the value customers receive, in relation to the story.

Now let's think about BDD. Here's a sample test from Test::Cukes:

use Test::Cukes;

feature(<<TEXT);
Feature: writing behavior tests
  In order to make me happy
  As a test maniac
  I want to write behavior tests

  Scenario: Hello World
    Given the test program is running
    When it reaches this step
    Then it should pass
TEXT

Given qr/the (.+) program is (.+)/, sub {
    my ($program_name, $running_or_failing) = @_;
    assert "running program '$program_name'";
};

When qr/it reaches this step/, sub {
    assert "reaches";
};

Then qr/it should pass/, sub {
    assert "passes";
};

runtests;

And the output:

1..3
ok 1 - Given the test program is running
ok 2 - When it reaches this step
ok 3 - Then it should pass

That's a silly example, but you can see how it can easily be tailored to work with the acceptance criteria listed above. When you have stories that are truly focused on customer needs, and acceptance criteria for said stories, BDD can allow you to better write tests that reflect the customer's needs. Or you can read the docs for Test::Cucumber::Tiny for an even better example. Multiple scenarios are created and driven through the "Given/Then/When" tests that Cucumber requires.

Yes, this requires that you be more customer-focused, but one of the biggest problems I encounter in many software shops today is devs focusing on technical issues and forgetting that there are real, live people at the other end.

My only concern is the same concern I have for Selenium: clients so frequently say "nice idea, but no." (Actually, I've several clients who've used Selenium and then forgot about it and now all of the tests fail, but that's another blog entry).

Nonetheless, if I see an appropriate opportunity to use BDD, I'm going to give it a go. That will likely require that the company's project management methodology be customer-focused and create appropriate stories, so I might have to start higher up the chain. Nonetheless, I think BDD is one of those "fads" that actually has something going for it.

4 Comments

I agree absolutely that programmers need to think about the stakeholders first and foremost.

I've been trying to like BDD, but the APIs I've seen are just wretched. One BDD framework has a function called "it()", which takes everything I know about reading code and tosses it out the window.

I don't read code like a paragraph, I read it looking for variables, functions, arguments, objects and methods. Trying to make a block of code read like a paragraph hurts my brain:

/* Example changed to remove camelCase and other style complaints */
describe( "A test suite", function() {
  it( "contains a function with an expectation", function() {
    expect( true ).to_be( true );
  } );
} );

Agreed. I'm more than mildly annoyed by the pseudo-english, and it's not very consistent: sometimes you separate words with _ like in to_be(), sometimes you put words in a string. And somehow I'm not convinced that the above example is "more BDD" than the good ol':

subtest "a test suite" => sub {
    ok(something_that_expects_to_return_true());
    is(supposed_to_return_2(), 2);
}

Another possible advantage to the BDD style is that the human readable tests cases, although seem contrived, can help people down the road understand why a given test was written. Very often I inherit projects with 5-7 year old code test suites and usually its very, very hard to figure out what a given test is for, and what story it meant to fix.

The general situation/problem you mention can also be described like this:

If you present problems, you get solutions. If you present solutions, you get problems.

About the story line: I'd suggest to ask What for? instead of Why?. What for? forces you to look into the future (what will I be able to do), while Why? often is looking into the past (where did I fail).

About Ovid

user-pic Freelance Perl/Testing/Agile consultant and trainer. See http://www.allaroundtheworld.fr/ for our services. If you have a problem with Perl, we will solve it for you. And don't forget to buy my book! http://www.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840/