Green in 30 mins : Getting Started with Cucumber (a test framework for BDD)

Prologue:
BDD is TDD with an outside-in (top-down) bent.
The way to tackle any new feature request is to ask Why 5 times? An you should arrive at one of Protect Revenue, Increase Revenue or Manage Cost. If not, chances are you’re building something that is not needed.
A feature can be summarized in a few lines as (ala Mike Cohn’s user story format)


As a <role>
I want <feature>
So that <value>



Dan North began this journey with JBehave.
It evolved over time into a Ruby gem - RSpec courtesy David Chelimsky n co. RSpec consisted of two parts - example runner and a story runner. The story runner runs features (think big cog) written up as plain text files which can be ‘executed’ (via some developer-added connecting-glue code in a separate location). The example runner (think small cog) is a bunch of module specs that make the feature happen. (think xUnit for BDD).
The RSpec story runner has now been simplified/improved and become Cucumber – a really neat little tool by Aslak Hellesoy. There are others too who have contributed to this movement like Dave Astels, et. all


You can still use RSpec example runner in tango with Cucumber or you could stick with xUnit under the hood instead of RSpec.
Enough prologue for today.

Now I've spent 3-4 days chasing hyperlinks and watching screencasts (links at the end of Post#2 in this series), this post should give you a rolling start on cucumber.


Step1# Installation

You need a few Ruby Gems to get rolling. (RSpec & Win32Console not mandatory)

gem install rspec
gem install cucumber
gem install win32console


I have rspec (1.2.8) | cucumber (0.3.99) | win32console (1.2.0). Try ‘cucumber –help’ to verify this step. Win32console is for color output on Windows.

Step2# Lets go bowling

I’ll take the popular ‘Score a bowling game’ TDD Kata as an example.
So we begin with a feature. (I’ll skip the pop-the-why-stack ; this feature falls into the ‘protect revenue’ category). Find a nice empty directory let’s say c:\cukes. Create a features subdirectory within it.
We create a new file : features\bowling_score.feature and type this in. This is called the feature narrative – it’s just a descriptive block of text. However the form is slightly different the context/value clause rises to the top with the prefix In order to <business value>. Spotlight on business value!
Note: Indentation matters! Spaces preferred.

Feature: Bowling Game Score Calculation
In order to let the player know his score
As a CEO of TheBowlingInc
I want to calculate the score at the end of a bowling fame


Next we Save And Run Cucumber from the c:\cukes directory. This step I hereby alias to SARC. This outputs

c:\cukes>cucumber
Feature: Bowling Game Score Calculation
In order to let the player know his score
As a CEO of TheBowlingInc
I want to calculate the score at the end of a bowling fame

0 scenarios
0 steps
0m0.000s


So now that prompts us : we need Scenarios. A Feature may contain multiple scenarios (which collectively validate the feature).
A Scenario is executable. A Scenario takes the form

Scenario: <text description>
Given <context>
When <action>
Then <user-visible outcome>


So we expand our feature like this.

Feature: Bowling Game Score Calculation
In order to let the player know his score
As a CEO of TheBowlingInc
I want to calculate the score at the end of a bowling fame

Scenario: All Gutters score 0
Given I am starting a new game
When I roll 20 gutter balls
Then the score should be 0
And the game should be over


In addition you can write And <step> to have multiple steps – the above example is equivalent to 2 Then clauses. You can use it under Given/When too. Save and run cucumber again. (or SARC from here on)

[cukes_01.jpg]

For some reason the snippets don’t show up. So we play a little trick. Create a subfolder under features called step_definitions. Create a new blank file called bowling_game_steps.rb in it. Run cucumber again. Now you should see some snippets. Copy them from the console and paste into the blank file. SARC. You should now see that the first step is shown as a TODO and the rest are skipped (and in a different step-color to boot)

The first snippet looks like


Given /^I am starting a new game$/ do
pending
end


We need to define a “step” – specify the action to be taken i.e. when cucumber encounters a matching Given clause for the regex, what action should it take? You define that as the content of the block. Let’s say we want to create a new instance of a game. So replace `pending` with
`@game = BowlingGame.new`
SARC and now we have a familiar color – Red (of the Red-Green-Refactor fame). We’re notified that our step has failed.


[cukes_02.jpg]

We have no class called Bowling Game yet. Create a new subfolder under features called support to house our app classes. Create a new file in there ; features\support\bowling_game.cs

class BowlingGame; end

SARC and we’re green…. partially.. better than red. Cucumber now points us to the next step.

[cukes_03.jpg]

I’ll define all the steps like this.. while you take a breather. There. The updated version

Given /^I am starting a new game$/ do
@game = BowlingGame.new
end

When /^I roll (\d+) gutter balls$/ do |count|
count.to_i.times{
@game.roll(0)
}
end

Then /^the score should be (\d+)$/ do |expected_score|
@game.score.should == expected_score.to_i
end

Then /^the game should be over$/ do
@game.should be_over
end

When /^my rolls are (.*)$/ do |rolls|
rolls.split(' ').each{|roll|
@game.roll(roll.to_i)
}
end



NOTE:
  1. The second step is an example of a parameterized step. If the regex in the step definition contains groups, the matched contents are passed in as parameters to the block for the step. Parameter passed in are strings so you need to convert it to the right type before use. Standard regexp rules apply for matching groups. So now the step can match both ‘When I roll 5 gutter balls’ and When I roll 20 gutter balls’. You also see parameters highlighted distinctly in cucumber output. Cool!
  2. Then steps use a new should method which is added to all objects. You can read more about such helper methods here and here
  3. Cucumber tells after each step what needs to be done next. This rhythm is similar to the TDD approach.


Here’s the updated bowling_game.rb with ‘the simplest thing that could possibly work’

class BowlingGame
def roll( pins_knocked_down)
end
def score
0
end
def over?
true
end
end


SARC this time we specify the no source
option as a CL argument

 >cucumber –s 


[cukes_04.jpg]
And we’ve reached the promised land - plain text file story/features that can be verified automatically at the push of a button. That's pretty cool.

Just to reiterate the folder structure.


[folder_hier.jpg]

In the next post, we move up the learning curve with Scenario tables and tagging.

No comments:

Post a Comment