cucumber
DESCRIPTION
Writing runnable acceptance criteria in plain text with Cucumber.TRANSCRIPT
Writing runnable acceptance criteria in plain text with
Farooq Ali
The YawningCrevasse of Doom
fluffy high-level needs
<technical_babble />
[Test][ExpectedException(typeof(InsufficientFundsException))]public void TransferWithInsufficientFunds(){
Account source = new Account();source.Deposit(200.00F);Account destination = new Account();destination.Deposit(150.00F);
}
the convergence of two movements
the convergence of two movementsBehavior-Driven Development (BDD)& Domain-Specific Languages (DSLs)
BDD movement
“Acceptance criteria should be executable”
“…a ubiquitous language for analysis”
what are we really trying to describe?
what are we really trying to describe?
the human concept of causality
Precondition -> Given
Action -> When
Outcome -> Then
Causality
Setup Data/State -> Given
Call Method -> When
Assert -> Then
Causality
“…a computer language that's targeted to a particular kind of problem, rather than a general purpose language that's aimed at any kind of software problem.”
DSL movement
“The sweet spot, however is in making DSLs business-readable rather than business-writeable.”
DSL movement
BDD + DSL
Feature: AdditionAs a math moronI want to add two numbers togetherSo that I can better use my scarce brain processing power Scenario: Adding positive numbers together Given I enter 1 into the calculator When I add 1 to it Then the calculator should show 2
Gherkin
Acceptance Criteria
Shared Vocabulary
Documentation
Traceability
Bug Tracking
Using Cucumber
Acceptance Criteria(Plain Text)
Step Definitions(Ruby)
ComponentsBuilding Blocks
Acceptance Criteria
Feature: AdditionAs a math moronI want to add two numbers togetherSo that I can better use my scarce brain processing power Scenario: Adding positive numbers together Given I enter 1 into the calculator When I add 1 to it Then the calculator should show 2
Acceptance Criteria
Acceptance Criteria
Feature: AdditionAs a math moronI want to add two numbers togetherSo that I can better use my scarce brain processing power Scenario: Adding positive numbers together Given I enter 1 into the calculator When I add 1 to it Then the calculator should show 2
Acceptance Criteria
Acceptance Criteria
Feature: AdditionAs a math moronI want to add two numbers togetherSo that I can better use my scarce brain processing power Scenario: Adding positive numbers together Given I enter 1 into the calculator When I add 1 to it Then the calculator should show 2
Acceptance Criteria
Acceptance Criteria
Feature: AdditionAs a math moronI want to add two numbers togetherSo that I can better use my scarce brain processing power Scenario: Adding positive numbers together Given I enter 1 into the calculator When I add 1 to it Then the calculator should show 2
Acceptance Criteria
Acceptance Criteria
Feature: AdditionAs a math moronI want to add two numbers togetherSo that I can better use my scarce brain processing power Scenario: Adding positive numbers together Given I enter 1 into the calculator When I add 1 to it Then the calculator should show 2
Acceptance Criteria
KeywordsKeyword Localized
name ‘English’
native ‘English’
encoding ‘UTF-8’
feature ‘Feature’
background ‘Background’
scenario ‘Scenario’
scenario_outline ‘Scenario Outline’
examples ‘Examples’
given ‘Given’
when ‘When’
then ’Then’
and ‘And’
but ‘But’
Keywords
LanguagesMacintosh-9:calculator ThoughtWorks$ cucumber --language help| ar | Arabic | العربية| bg | Bulgarian | български | cat | Catalan | català | cy | Welsh | Cymraeg | cz | Czech | Česky | da | Danish | dansk | de | German | Deutsch | en | English | English | en-au | Australian | Australian | en-lol | LOLCAT | LOLCAT | en-tx | Texan | Texan | es | Spanish | español | et | Estonian | eesti keel | fi | Finnish | suomi | fr | French | français | he | Hebrew | עברית | hr | Croatian | hrvatski | hu | Hungarian | magyar | id | Indonesian | Bahasa Indonesia
Languages
Given I enter 1 into the calculatorWhen I add 1 to itThen the calculator should show 2
Acceptance Criteria(Plain Text)
Step DefinitionsBuilding Blocks
Given I enter 1 into the calculatorWhen I add 1 to itThen the calculator should show 2
Given /^I enter (.+) into the calculator$/ do |number| @calculator = Calculator.new @calculator.enter(number.to_i)end
When /^I add (.+) to it$/ do |number| @calculator.add(number.to_i)end
When /^the calculator should show (.+)$/ do |number| @calculator.number.should == number.to_iend
Acceptance Criteria(Plain Text)
Step Definitions(Ruby)
Step DefinitionsBuilding Blocks
Step Definitions
Given /^I enter (.+) into the calculator$/ do |number| @calculator = Calculator.new @calculator.enter(number.to_i)end
When /^I add (.+) to it$/ do |number| @calculator.add(number.to_i)end
Then /^the calculator should show (.+)$/ do |number| @calculator.number.should == number.to_iend
Step Definitions
Step Definitions
Then /^the calculator should show (.+)$/ do |number| @calculator.number.should == number.to_iend
Step Definitions
Step DefinitionsStep Definitions
When I give a 45-minute long talk on Cucumber at 10:30am
Step DefinitionsStep Definitions
When I give a 45-minute long talk on Cucumber at 10:30am
Step Definitions
/I give a (\d+)-minute long talk on (.+) at (\d+):(\d+)(am|pm)/
Step Definitions
When I give a 45-minute long talk on Cucumber at 10:30am
Step Definitions
/I give a (\d+)-minute long talk on (.+) at (\d+):(\d+)(am|pm)/
Step Definitions
When I give a 45-minute long talk on Cucumber at 10:30am
Step DefinitionsStep Definitions
do |duration_in_minutes, topic, start_hour, start_minute, am_pm| end
/I give a (\d+)-minute long talk on (.+) at (\d+):(\d+)(am|pm)/
When I give a 45-minute long talk on Cucumber at 10:30am
Multiple Given-When-Thens
Scenario: Multiple Givens Given one thing Given another thing Given yet another thing When I open my eyes Then I see something Then I don't see something else
Multiple Given-When-Thens
Or use And / But
Scenario: Multiple Givens Given one thing And another thing And yet another thing When I open my eyes Then I see something But I don't see something else
Or use And/But
Reuse
Given /^the user (.*) exists$/ do |name|#...
end
Given /^I log in as (.*)$/ do |name| #...end
Given /^(.*) is logged in$/ do |name| Given "the user #{name} exists" Given "I log in as #{name}"end
Calling steps from within steps
ReuseMultiline step arguments - Tables
Given the following people exist: | name | email | phone | | Farooq | [email protected] | 123 | | Mary | [email protected] | 234 | | Bob | [email protected] | 456 |
ReuseMultiline step arguments - Tables
Given the following people exist: | name | email | phone | | Farooq | [email protected] | 123 | | Mary | [email protected] | 234 | | Bob | [email protected] | 456 |
Given /the following people exist:/ do |people_table| people_table.hashes.each do |hash| # 1st: {'name' => ’Farooq', 'email' => ’[email protected]’, ... } # 2nd: {'name' => ’Mary', 'email' => ’[email protected]', ... } # ... endend
ReuseMultiline step arguments - Strings
Given a blog post named "Random" with Markdown body """ Some Title, Eh? ============== Here is the first paragraph of my blog post. Lorem ipsum dolor sit amet, consectetur adipiscing elit. """
ReuseMultiline step arguments - Strings
Given a blog post named "Random" with Markdown body """ Some Title, Eh? ============== Here is the first paragraph of my blog post. Lorem ipsum dolor sit amet, consectetur adipiscing elit. """
Given /^a blog post named "([^\"]*)" with Markdown body$/ do |title, markdown| Post.create!(:title => title, :body => markdown)end
Tags
@billingFeature: Enter billing info
@creditcard Scenario: Credit card @paypal Scenario: PayPal
Tags
Hooks
Before do # do something before first step of scenarioend
After do # do something after each scenarioend
Hooks
Tagged Hooks
Before('@billing', '@calculations') do # This will only run before scenarios tagged
# with @billing or @calculations. end
Tagged Hooks
Global Hooks
browser = Selenium::SeleniumDriver.new("localhost", 4444, "*chrome",
"http://localhost", 15000)
Given 'I am on the Google search page' do browser.open('http://www.google.com/’)end
When /I search for "(.*)"/ do |query| browser.type('q', query) browser.click 'btnG’ browser.wait_for_page_to_loadend
Then /I should see a link to (.*)/ do |expected_url| browser.is_element_present("css=a[href='#{expected_url}']").should be_trueend
Global Hooks
Tags
Feature: Multiple site support As a Mephisto site owner I want to host blogs for different people In order to make gigantic piles of money
Background: Given a global administrator named “Greg” And a blog named “Greg’s anti-tax rants” And a customer named “Dr. Bill” And a blog named “Expensive Therapy” owned by “Dr. Bill”
Scenario: Dr. Bill posts to his own blog Given I am logged in as Dr. Bill When I try to post to “Expensive Therapy” Then I should see “Your article was published.”
Backgrounds
Global Hooks
# support fileTransform /^user (\w+)$/ do |username| User.find_by_username(username) end
# step definition fileThen /^(user \w+) should be friends with (user \w+)$/ do |user,friend| user.should be_friends_with(friend)end
Step Argument Transforms
Formatters
cucumber -f junit -–out <output_dir>
Formatters: StepsFeature: --formatter steps option - Steps Formatter
In order to easily see which steps are already defined, specially when using 3rd party steps libraries, Cucumber should show the available steps in a user-friendly format
Background: Given I am in steps_library
Scenario: Printing steps When I run cucumber -f steps features Then it should pass with """ features/step_definitions/steps_lib1.rb /^I defined a first step$/ # features/step_definitions/steps_lib1.rb:1 /^I define a second step$/ # features/step_definitions/steps_lib1.rb:4 /^I should also have a third step$/ # features/step_definitions/steps_lib1.rb:7 features/step_definitions/steps_lib2.rb /^I defined a step 4$/ # features/step_definitions/steps_lib2.rb:1 /^I create a step 5$/ # features/step_definitions/steps_lib2.rb:4 /^I should be too tired for step 6$/ # features/step_definitions/steps_lib2.rb:7 6 step definition(s) in 2 source file(s).
"""
Formatters : JUnitFeature: JUnit output formatter In order for developers to create test reports with ant Cucumber should be able to output JUnit xml files Background: Given I am in junit And the tmp directory is empty @mri186 Scenario: one feature, one passing scenario, one failing scenario When I run cucumber --format junit --out tmp/ features/one_passing_one_failing.feature Then it should fail with """
""" And "examples/junit/tmp/TEST-one_passing_one_failing.xml" with junit duration "0.005" should contain """ <?xml version="1.0" encoding="UTF-8"?> <testsuite errors="0" failures="1" name="One passing scenario, one failing scenario" tests="2"
time="0.005"> <testcase classname="One passing scenario, one failing scenario.Passing" name="Passing"
time="0.005"> </testcase> <testcase classname="One passing scenario, one failing scenario.Failing" name="Failing"
time="0.005"> <failure message="failed Failing" type="failed"> Scenario: Failing
Given a failing scenario
Message: (RuntimeError) features/one_passing_one_failing.feature:7:in `Given a failing scenario' </failure> </testcase> </testsuite>
""" Scenario: pending steps are simply skipped When I run cucumber --format junit --out tmp/ features/pending.feature Then it should pass with """ """ And "examples/junit/tmp/TEST-pending.xml" with junit duration "0.009" should contain """ <?xml version="1.0" encoding="UTF-8"?> <testsuite errors="0" failures="0" name="Pending step" tests="0" time="0.009"> </testsuite> """
Scenario: pending step with strict option should fail When I run cucumber --format junit --out tmp/ features/pending.feature --strict Then it should fail with """
""" And "examples/junit/tmp/TEST-pending.xml" with junit duration "0.000160" should contain """ <?xml version="1.0" encoding="UTF-8"?> <testsuite errors="0" failures="1" name="Pending step" tests="1" time="0.000160"> <testcase classname="Pending step.Pending" name="Pending" time="0.000160"> <failure message="pending Pending" type="pending"> Scenario: Pending
TODO (Cucumber::Pending) features/pending.feature:4:in `Given a pending step' </failure> </testcase> </testsuite>
""" Scenario: run all features When I run cucumber --format junit --out tmp/ features Then it should fail with """ """ And "examples/junit/tmp/TEST-one_passing_one_failing.xml" should exist And "examples/junit/tmp/TEST-pending.xml" should exist Scenario: show correct error message if no --out is passed When I run cucumber --format junit features Then STDERR should not match """can't convert .* into String \(TypeError\) """ And STDERR should match """You \*must\* specify \-\-out DIR for the junit formatter """
Formatters : HTML
Feature: HTML formatter In order to make it easy to read Cucumber results there should be a HTML formatter with an awesome CSS
Scenario: Everything in examples/self_test When I run cucumber -q --format html --out tmp/a.html features Then "examples/self_test/tmp/a.html" should have the same contents as
"features/html_formatter/a.html"