Upgrade to Pro — share decks privately, control downloads, hide ads and more …

BDD with Cucumber

BDD with Cucumber

Talk from Suffolk Developers, 30 March 2015.

Notes:

What it is
What it's for
How to get the most out of it

Cucumber.js, Cucumber-JVM, SpecFlow (.NET), Behat (PHP), and others

Plain text description of expected behaviour

Just text for information
Format optional (see as a/I want to/so that)
Can be nothing, or a whole page (Relish)

One or more scenarios per feature
eg checkout feature: check out with card, with paypal etc

First line just descriptive again

Steps are meat of scenario
Text is matched to executable step definitions (see later)

Given: set up preconditions
When: do the thing you’re describing
Then: check you got the expected result
Also And, But, *

A more realistic feature
When we run it …

Steps initially undefined
Provides skeleton definitions to get you started
Regular expression with capture groups
Used to be built-in steps for common web actions …

web_steps.rb – “training wheels” but often overused
Deprecated then removed
Why is it bad? …

Leads to scenarios like this (one of mine) – loads of them!
212 feature files, 1143 scenarios, 9210 steps, 45 minutes.
Lots of detail in the features, hiding what’s important
What happens when you show these to customers/business/stakeholders?

Back to web_steps example
So how can we improve this? …

Replace procedural with declarative
Just show what’s important
Of course we now have to define our own steps …

Using Capybara – simple DSL

Once you start talking about the feature in simple declarative steps, it’s easier to uncover other pieces of behaviour
Important to get words right (ubiquitous language) – same terms from business people down to classes and methods

If everyone uses the same language, there are fewer misunderstandings, and the code’s easier to understand

Of course you can go too far!

Not just developers – whole team!
Developers, testers, customers, users, business analysts …
“… web applications” … speaking of which …

Selenium-webdriver, using Firefox
Headless browser (eg Poltergeist) is 4–5x faster
For non-javascript (in Ruby), use rack-test (twice as fast again)
Tag scenarios which need javascript

Not just web.
Aruba: command line apps
Calabash: Android and iOS

A feature with several scenarios

Duplication!
In this case just one step, but could be more

Extract to background
Runs before each scenario

Another example – different kind of duplication
(lots of similar steps)

Single step with a table
Table is passed to step definition for parsing

Yet another kind of duplication
Several similar scenarios

Convert to scenario outline
Executes scenario once for each row
Substitutes values
Use sparingly!

Ubiquitous language doesn’t have to be English!
Comes with support for ~40 languages
Easy to add more

Example from the documentation

Some slightly less serious languages!
Also pirate, Texan, Scouse, Australian

Keep everything organised!
Already seen techniques for features – what about step definitions

Some step definitions from our earlier login example

These share a lot of code
Field names etc could be repeated all over
All need to be modified if app changes

Extract to a method in a module
Include the module in cucumber’s ‘world’
In general aim for small step definitions calling methods in well-organised modules
See also Page Object pattern

Common confusion: acceptance tests have to be end-to-end
eg do everything through a web UI
Makes them slow!

Alastair Cockburn’s ‘hexagonal architecture’
Puts business logic at the centre, with adapters for UI, persistence etc
Test business logic directly (plus limited E2E confidence tests)

Like TDD, cucumber’s not really primarily about testing

Helps develop a shared understanding of how the system should behave
Reduce unused features and rework

Cucumber features provide a framework for simply documenting the shared understanding of behaviour
… and make that documentation executable to prove it’s still true

BDD: driving development with concrete usage examples based on conversation with customers and users

Good books from PragProg

Hopefully passed!

Questions?

Kerry Buckley

March 30, 2015
Tweet

More Decks by Kerry Buckley

Other Decks in Technology

Transcript

  1. Behaviour-driven development with Cucumber Kerry Buckley (@kerryb) Suffolk Developers, 30

    March 2015 http://www.flickr.com/photos/yogendra174/4665988849
  2. Feature: Suffolk Developers talk In order to fill the schedule

    As a Cucumber user I want to talk about BDD with Cucumber Scenario: "BDD with Cucumber" talk Given I have managed to prepare a talk And there are people there to hear it When I give the talk Then the audience should learn something
  3. Feature: Suffolk Developers talk In order to fill the schedule

    As a Cucumber user I want to talk about BDD with Cucumber Scenario: "BDD with Cucumber" talk Given I have managed to prepare a talk And there are people there to hear it When I give the talk Then the audience should learn something
  4. Feature: Suffolk Developers talk In order to fill the schedule

    As a Cucumber user I want to talk about BDD with Cucumber Scenario: "BDD with Cucumber" talk Given I have managed to prepare a talk And there are people there to hear it When I give the talk Then the audience should learn something
  5. Feature: Suffolk Developers talk In order to fill the schedule

    As a Cucumber user I want to talk about BDD with Cucumber Scenario: "BDD with Cucumber" talk Given I have managed to prepare a talk And there are people there to hear it When I give the talk Then the audience should learn something
  6. Feature: Suffolk Developers talk In order to fill the schedule

    As a Cucumber user I want to talk about BDD with Cucumber Scenario: "BDD with Cucumber" talk Given I have managed to prepare a talk And there are people there to hear it When I give the talk Then the audience should learn something
  7. Feature: Suffolk Developers talk In order to fill the schedule

    As a Cucumber user I want to talk about BDD with Cucumber Scenario: "BDD with Cucumber" talk Given I have managed to prepare a talk And there are people there to hear it When I give the talk Then the audience should learn something
  8. Feature: Withdraw cash from account Scenario: Successful withdrawal Given I

    have £100 in my account When I request a withdrawal of £20 Then I should receive £20 And my balance should be £80
  9. $ cucumber withdraw_cash.feature Feature: Withdraw cash from account Scenario: Successful

    withdrawal # withdraw_cash.feature:3 Given I have £100 in my account # withdraw_cash.feature:4 When I request a withdrawal of £20 # withdraw_cash.feature:5 Then I should receive £20 # withdraw_cash.feature:6 And my balance should be £80 # withdraw_cash.feature:7 1 scenario (1 undefined) 4 steps (4 undefined) 0m0.002s You can implement step definitions for undefined steps with these snippets: Given /^I have £(\d+) in my account$/ do |arg1| pending # express the regexp above with the code you wish you had end ...
  10. Scenario: Successful login Given a user "Amy" with password "secret"

    When I go to the login page And I fill in "User name" with "Amy" And I fill in "Password" with "secret" And I press "Log in" Then I should see "Welcome, Amy"
  11. Scenario: Adding a CaaS Linux Service in a production (or

    test) environment Given a fred production environment exists And I am on the last deployment page When I select "CaaS Linux Service" from "Add a new" And I press "Add service" And I fill in the following: | Role | FTP Server | | Application Installed Software | None | And I select "SC3 (redside)" from "Security Domain" And I select "2" from "Virtual CPUs" And I select "2" from "Virtual Memory" And I select "OEL 5u6 32bit" from "Operating System" And I select "None" from "MaaS(EMB) MQ Client" And I select "None" from "Oracle Client" And I check "Netbackup Client" And I check "XFB Client" And I check "BT Hunter Client" And I check "BTBPTM Client" And I check "Web Tier" And I press "Save" Then I should be on the last deployment page And I should see "Service saved" And I should see ...
  12. Scenario: Successful login Given a user "Amy" with password "secret"

    When I go to the login page And I fill in "User name" with "Amy" And I fill in "Password" with "secret" And I press "Log in" Then I should see "Welcome, Amy"
  13. Scenario: User is greeted upon login Given the user "Amy"

    has an account When she logs in Then she should see "Welcome, Amy"
  14. Given /^the user "(.*?)" has an account$/ do |name| @user

    = User.find_or_create_by_name name, password: "secret" end When /^s?he logs in$/ do visit "/login" fill_in "User name", with: @user.name fill_in "Password", with: @user.password click_button "Log in" end Then /^s?he should see "(.*?)"$/ do |text| page.should have_content(text) end
  15. Scenario: User is greeted upon login Given the user "Amy"

    has an account When she logs in Then she should see "Welcome, Amy" Scenario: Secure pages require login When I visit a secure page as a guest Then I should be asked to log in When I log in with a valid account Then I should see the secure page
  16. Feature: My awesome system Scenario: Using the system Given the

    system exists When I use it Then everything should work perfectly
  17. Feature: Posting messages to friends Scenario: Posting a text message

    Given I am logged in When I post a text message Then my friends should see my message Scenario: Posting a photo Given I am logged in When I post a photo Then my friends should see a thumbnail of my photo And clicking the thumbnail should show the photo
  18. Feature: Posting messages to friends Scenario: Posting a text message

    Given I am logged in When I post a text message Then my friends should see my message Scenario: Posting a photo Given I am logged in When I post a photo Then my friends should see a thumbnail of my photo And clicking the thumbnail should show the photo
  19. Feature: Posting messages to friends Background: Given I am logged

    in Scenario: Posting a text message When I post a text message Then my friends should see my message Scenario: Posting a photo When I post a photo Then my friends should see a thumbnail of my photo And clicking the thumbnail should show the photo
  20. Scenario: When I add 1 widget to my order And

    I add 5 doodahs to my order And I add 2 thingummies to my order Then my basket total should be £12.34
  21. Scenario: When I add the following to my order: |

    Quantity | Item | | 1 | widget | | 5 | doodah | | 2 | thingummy | Then my basket total should be £12.34
  22. Scenario: Simple addition When I calculate 2 + 2 Then

    the answer should be 4 When I calculate 10 + 5 Then the answer should be 15 Scenario: Simple multiplication When I calculate 2 * 2 Then the answer should be 4 When I calculate 6 * 7 Then the answer should be 42
  23. Scenario Outline: Simple arithmetic When I calculate <first> <operation> <second>

    Then the answer should be <answer> Examples: | first | operation | second | answer | | 2 | + | 2 | 4 | | 10 | + | 5 | 15 | | 2 | * | 2 | 4 | | 6 | * | 7 | 42 |
  24. # language: fr Fonctionnalité: Addition Afin de financer mon bonus

    avec l'argent des pigeons En tant que trader Je souhaite pouvoir additionner 2 chiffres Plan du Scénario: Addition de produits dérivés Soit une calculatrice Etant donné qu'on tape <a> Et qu'on tape <b> Lorsqu'on tape additionner Alors le résultat doit être <somme> Exemples: | a | b | somme | | 2 | 2 | 4 | | 2 | 3 | 5 |
  25. # language: es Característica: adición Para evitar hacer errores tontos

    Como un matemático idiota Quiero saber la suma de los números Esquema del escenario: Sumar dos números Dado que he introducido <entrada_1> en la calculadora Y que he introducido <entrada_2> en la calculadora Cuando oprimo el <botón> Entonces el resultado debe ser <resultado> en la pantalla Ejemplos: | entrada_1 | entrada_2 | botón | resultado | | 20 | 30 | add | 50 | | 2 | 5 | add | 7 | | 0 | 40 | add | 40 |
  26. # language: de Funktionalität: Addition Um dumme Fehler zu vermeiden

    möchte ich als Matheidiot die Summe zweier Zahlen gesagt bekommen Szenariogrundriss: Zwei Zahlen hinzufügen Angenommen ich habe <Eingabe_1> in den Taschenrechner eingegeben Und ich habe <Eingabe_2> in den Taschenrechner eingegeben Wenn ich <Knopf> drücke Dann sollte das Ergebniss auf dem Bildschirm <Ausgabe> sein Beispiele: | Eingabe_1 | Eingabe_2 | Knopf | Ausgabe | | 20 | 30 | add | 50 | | 2 | 5 | add | 7 | | 0 | 40 | add | 40 |
  27. # language: en-lol OH HAI: STUFFING MISHUN: CUCUMBR I CAN

    HAZ IN TEH BEGINNIN 3 CUCUMBRZ WEN I EAT 2 CUCUMBRZ DEN I HAS 2 CUCUMBERZ IN MAH BELLY AN IN TEH END 1 CUCUMBRZ KTHXBAI
  28. Given /^the user "(.*?)" has an account$/ do |name| @user

    = User.find_or_create_by_name name, password: "secret" end Given /^a logged-in user$/ do @user = User.find_or_create_by_name "Fred", password: "secret" visit "/login" fill_in "User name", :with => @user.name fill_in "Password", :with => @user.password click_button "Log in" end When /^s?he logs in$/ do visit "/login" fill_in "User name", with: @user.name fill_in "Password", with: @user.password click_button "Log in" end Then /^s?he should see "(.*?)"$/ do |text| page.should have_content(text) end
  29. Given /^the user "(.*?)" has an account$/ do |name| @user

    = User.find_or_create_by_name name, password: "secret" end Given /^a logged-in user$/ do @user = User.find_or_create_by_name "Fred", password: "secret" visit "/login" fill_in "User name", :with => @user.name fill_in "Password", :with => @user.password click_button "Log in" end When /^s?he logs in$/ do visit "/login" fill_in "User name", with: @user.name fill_in "Password", with: @user.password click_button "Log in" end Then /^s?he should see "(.*?)"$/ do |text| page.should have_content(text) end
  30. module LoginSteps def login(name, password) visit "/login" fill_in "User name",

    with: name fill_in "Password", with: password click_button "Log in" end end World(LoginSteps)
  31. module LoginSteps def login(name, password) visit "/login" fill_in "User name",

    with: name fill_in "Password", with: password click_button "Log in" end end World(LoginSteps) Given /^a logged in user$/ do @user = User.find_or_create_by_name name, password: "secret" login(@user.name, @user.password) end When /^s?he logs in$/ do login(@user.name, @user.password) end
  32. $ cucumber talk.feature Feature: Suffolk Developers talk In order to

    fill the schedule As a Cucumber user I want to talk about BDD with Cucumber Scenario: "BDD with Cucumber" talk # talk.feature:6 Given I have managed to prepare my talk on time # talk_steps.rb:1 And there are people there to hear it # talk_steps.rb:4 When I give the talk # talk_steps.rb:7 Then the audience should learn something # talk_steps.rb:10 1 scenario (1 passed) 4 steps (4 passed) 0m0.002s