Introduction to BDD with Cucumber
Cucumber is a framework for writing and executing high level descriptions of your software's functionality. Call these tests, examples, specifications, whatever... it doesn't matter too much. What I'm talking about has traditionally been called functional, integration, and/or system tests. In XP terms this includes tests called Story Tests, Customer Tests, and/or Acceptance Tests.One of Cucumber's most compelling features is that it provides the ability to write these descriptions using plain text in your native language. Cucumber's language, Gherkin, is usable in a growing variety of human languages, including LOLZ. The advantage of this is that these feature descriptions can be written and/or understood by non-technical people involved in the project.
One important thing to keep in mind is that Cucumber is NOT a replacement for RSpec, test/unit, etc. It is not a low level testing/specification framework.
Cucumber plays a central role in a development approach called Behaviour Driven Development (BDD).
A Bit About BDD
Dan North describes BDD as “writing software that matters” in The RSpec Book and outlines 3 principles:- Enough is enough: do as much planning, analysis, and design as you need, but no more.
- Deliver stakeholder value: everything you do should deliver value or increase your ability to do so.
- It's a behavior: everyone involved should have the same way of talking about the system and what it does.
Features
A feature is something that your software does (or should do), and generally corresponds to a user story and a way to tell when it's finished and that it works.The general format of a feature is:
Feature: <short description>
<story>
<scenario 1>
...
<scenario n>
As a <role>
I want <feature>
so that <business value>
- Who's using the system?
- What are they doing?
- Why do they care?
As a code-breaker
I want to start a game
So that I can break the code
Scenarios
A feature is defined by one or more scenarios. A scenario is a sequence of steps through the feature that exercises one path. Cucumber uses the BDD style that Dan North put forth with his jBehave project: given-when-then.A scenario is made up of 3 sections related to the 3 types of steps:
- Given: This sets up preconditions, or context, for the scenario. It works much like the
setup
in xUnit andbefore
blocks in RSpec. - When: This is what the feature is talking about, the action, the behaviour that we're focused on.
- Then: This checks postconditions… it verifies that the right thing happen in the When stage.
Scenario: <description>
<step 1>
…
<step n>
Scenario: start game
Given I am not yet playing
When I start a new game
Then the game should say “Welcome to CodeBreaker”
And the game should say “Enter guess:”
Then the game should say “Welcome to CodeBreaker”
Then the game should say “Enter guess:”
Then the game should say “Welcome to CodeBreaker”
But the game should not say “Save the cheerleader, save the world”
Implementing Steps
So here's our feature:Feature: code-breaker starts game
As a code-breaker
I want to start a game
So that I can break the code
Scenario: start game
Given I am not yet playing
When I start a new game
Then the game should say “Welcome to CodeBreaker”
And the game should say “Enter guess:”
1 scenario (1 undefined)
4 steps (4 undefined)
0m0.001s
You can implement step definitions for undefined steps with these snippets:
Given /^I am not yet playing$/ do
pending
end
When /^I start a new game$/ do
pending
end
Then /^the game should say “Welcome to CodeBreaker”$/ do
pending
end
Then /^the game should say “Enter guess:”$/ do
pending
end
1 scenario (1 pending)
4 steps (3 skipped, 1 pending)
Notice that the suggested steps are completely concrete. While that is fine some of the time, you will generally want to refactor steps and make them more generic and reusable. Notice that there are two then step definitions that are much the same:
Then /^the game should say “Welcome to CodeBreaker”$/ do
end
Then /^the game should say “Enter guess:”$/ do
end
Then /^the game should say “(.*)”$/ do |message|
end
Given /^I am not yet playing$/ do
@game = Codebreaker::Game.new
end
When /^I start a new game$/ do
end
When /^I start a new game$/ do
@messages = @game.start
end
@messages
that can be used in subsequent steps.Recall how we left our then step definition:
Then /^the game should say “(.*)”$/ do |message|
end
Then /^the game should say “(.*)”$/ do |message|
@messages.should include(message)
end
Earlier I mentioned that Cucumber supports various languages including LOLZ. Here's a feature in LOLZ:
OH HAI: STUFFING
MISHUN: CUCUMBR
I CAN HAZ IN TEH BEGINNIN 3 CUCUMBRZ
WEN I EAT 2 CUCUMBRZ
DEN I HAZ 2 CUCUMBERZ IN MAH BELLY
AN IN TEH END 1 CUCUMBRZ KTHXBAI
ICANHAZ /^IN TEH BEGINNIN (d+) CUCUMBRZ$/ do |n|
@basket = Basket.new(n.to_i)
end
WEN /^I EAT (d+) CUCUMBRZ$/ do |n|
@belly = Belly.new
@belly.eat(@basket.take(n.to_i))
end
DEN /^I HAZ (d+) CUCUMBERZ IN MAH BELLY$/ do |n|
@belly.cukes.should == n.to_i
end
DEN /^IN TEH END (d+) CUCUMBRZ KTHXBAI$/ do |n|
@basket.cukes.should == n.to_i
end
No comments:
Post a Comment