Monday, December 15, 2008

Why I Don't Use "Given, When, Then"

In Introducing BDD, Dan North states that he and Chris Matts were trying to develop a template that, "had to be loose enough that it wouldn’t feel artificial or constraining to analysts but structured enough that we could break the story into its constituent fragments and automate them." This gave birth to Given, When, Then syntax. One example that is given in the article is:

Given the account is in credit
And the card is valid
And the dispenser contains cash
When the customer requests cash
Then ensure the account is debited
And ensure cash is dispensed
And ensure the card is returned

This certainly has structure. But, I personally feel this is still a bit too artificial. Perhaps it is the large amount of content related to the context (account is in credit, the card is valid, the dispenser contains cash). I try to avoid specifying behaviors that have this many moving parts. I would prefer to see a behavior like:

The dispenser when the account is in credit should dispense cash.
The dispenser when the account is in credit should debit the account.
The dispenser when all transactions are complete should ensure the card is returned

I want to read sentences. When possible, I want to read short sentences. Given When Then generates a matrix of sentences to be parsed. GWT also saves some space in the report, but I gladly give that space back to have behavioral sentences that are easier to read quickly.

So, how do I keep the language regular and well-formed? The answer shows up more in the source code of the behvaiors than in the report. So, I might write the following behavior using EasySpec (in Groovy):


@EasySpec(interest='The dispenser')
class Dispenser_happy_path_Test extends GroovyTestCase() {

def account = new Account(balance:1000)
def dispenser = new Dispenser(available:5000)

@Context('when the account is in credit and the dispenser has cash')
public void setUp() {
dispenser.dispense(account, 100)
}

@Behavior
void test_should_debit_the_account() {
assertEquals(900, account.balance)
}

@Behavior
void test_should_dispense_the_requested_cash() {
assertEquals(100, dispenser.totalDespensed)
assertEquals(4900, dispenser.available)
}

@Behavior
void test_should_return_the_card() {
assertTrue(dispenser.lastCardReturned)
}
}

Upon running the EasySpec report, the user will get the following behaviors:


The dispenser when the account is in credit and the dispenser has cash should debit the account

The dispenser when the account is in credit and the dispenser has cash should dispense the requested cash

The dispenser when the account is in credit and the dispenser has cash should return the card


Easy Spec actually generates reports with nice formatting like this.

Perhaps all of this is just personal preference. But, I do find it easy to go back to old specifications and understand what is going on. Language and fluency are important.

2 comments:

Skelter said...

Still feels upside-down to me. I feel I'm reading someone who wants to be as wordy as Tolkein. Feels more natural to say

The dispenser should dispense cash when the account has credit
The dispenser should refuse to dispense cash when the account does not have credit.
The dispenser should return the card when all transactions are complete.
The dispenser should keep the card and taze the user if the user pin retry threshold is exceeded.

Rod Paddock said...

Eric... this is a great post just for explaing the structure of BDD tests. Not sure if that was your goal but it is a nice clear example of it.