The document discusses improving quality in software projects through behavior-driven development (BDD) and Cucumber features. It covers best practices for writing expressive Cucumber scenarios in Gherkin, including using a declarative style, backgrounds, tables, page objects, and matchers. The presentation emphasizes focusing scenarios on "what" should happen rather than "how" and avoiding tightly coupling scenarios to implementation details.
4. Amir Barylko - Better Cucumber Features
Why Projects Fail?
• Delivering late or over budget
• Delivering the wrong thing
• Unstable in production
• Costly to maintain
5. Amir Barylko - Better Cucumber Features
Improve Quality
• Unit Testing
• Integration Testing
• Acceptance Testing
6. Amir Barylko - Better Cucumber Features
BDD
• Implementing an application
• by describing its behavior
• from the perspective of the stakeholder
7. Amir Barylko - Better Cucumber Features
Outside In Approach
BDD TDD
8. Amir Barylko - Better Cucumber Features
Test First
• Write a test before any line of code
• Write minimum amount of code to make
the test pass
• Refactor code to eliminate “smells”
10. Amir Barylko - Better Cucumber Features
Gherkin DSL
• Business readable DSL
• Flush out requirements
• Documentation
• Automated testing
• Used by Cucumber,
SpecFlow, jBehave
11. Amir Barylko - Better Cucumber Features
Gherkin Keywords
• Feature • Then
• Scenario • And
• Given • But
• When
12. Amir Barylko - Better Cucumber Features
Features
Feature: Listing projects
As a user Free text!
I Want to see the list of projects
So I can choose one to see the details
Scenario: List all projects
(steps here to implement scenario)
Scenario: No projects are available
(steps here to implement scenario)
13. Amir Barylko - Better Cucumber Features
Scenario
Scenario: List all projects
Given I'm logged in Step 1
And I have some projects stored Step 2
When I browse the projects Step 3
Then I should see all of them listed Step 4
14. Amir Barylko - Better Cucumber Features
Running Features
• Parse the feature
• Parse the scenario
• For each scenario
• Find a step implementation
• Execute the code
15. Amir Barylko - Better Cucumber Features
Matching Step
• Matching regular expression
Given I have some projects stored Feature File
Given /^I have some projects stored$/
Step Def File
16. Amir Barylko - Better Cucumber Features
Step
Given /^I have some projects stored$/ do
projects = 10.times { random_valid_project }
fake_response = create_response(projects)
FakeWeb.register_uri(....)
end
Plain
Ruby!
18. Amir Barylko - Better Cucumber Features
What we want?
• Readability
• Ubiquitous Language
• Consistent use of terminology
• Express natural business intent
• Avoid technical aspects
19. Amir Barylko - Better Cucumber Features
Imperative style
Scenario: Redirect user to originally requested page
Given a User "dave" exists with password "secret"
And I am not logged in
When I navigate to the home page
Then I am redirected to the login form
When I fill in "Username" with "dave"
And I fill in "Password" with "secret"
And I press "Login"
20. Amir Barylko - Better Cucumber Features
What’s the problem?
• Who needs the passwords?
• Tightly coupled to page implementation
• Lacks domain language
• Brittle tests
• Does not tell a story (boring)
21. Amir Barylko - Better Cucumber Features
Declarative style
Scenario: Redirect user to originally requested page
Given I am an authenticated user
When I attempt to view restricted content
Then I am presented with a login form
When I authenticated with valid credentials
Then I should be shown the restricted content
22. Amir Barylko - Better Cucumber Features
Declarative vs Imperative
• Imperative is associated to “how” to do it
• Declarative is associated to “what” we
want
• Where’s the boundary?
23. Amir Barylko - Better Cucumber Features
Too abstract?
Scenario: The whole system
Given the system exists
When I use it
Then it should work, perfectly
24. Amir Barylko - Better Cucumber Features
Background steps
• Steps may have some degree of repetition
• Because they start with the same “state”
• So they share the first X steps
25. Amir Barylko - Better Cucumber Features
Similar scenarios
Scenario: Change Password
Given I am logged in
And I choose to change my password
When I enter a new password
Then my password should be changed
Scenario: Change Password with same credentials
Given I am logged in
And I choose to change my password
When I enter the same password
Then I should see an error message explaining the problem
26. Amir Barylko - Better Cucumber Features
Create Background
Background: I want to change my password
Given I am logged in
And I choose to change my password
Scenario: Change Password
When I enter a new password
Then my password should be changed
Scenario: Change Password with same credentials
When I enter the same password
Then I should see an error message explaining the problem
27. Amir Barylko - Better Cucumber Features
Using Tables
• Sometimes data is hard to put in a step
• with multiple entries
Scenario: Listing movies
Given the movie “Blazing saddles” released “7 Feb 1974”
And the movie “Young Frankenstein” released “15 Dec 1974”
And the movie “The Producers” released “10 Nov 1968”
28. Amir Barylko - Better Cucumber Features
That’s boring!
• Express data in tabular form
Scenario: Listing movies
Given these movies:
| title | release |
| Blazing saddles | 7 Feb 1974 |
| Young Frankenstein | 15 Dec 1974 |
| The Producers | 10 Nov 1968 |
29. Amir Barylko - Better Cucumber Features
Or just a list
• Don’t use the header
Scenario: Listing movies
Given these movies:
| Blazing saddles |
| Young Frankenstein |
| The Producers |
30. Amir Barylko - Better Cucumber Features
Why the detail though?
• Do you really need the list?
Scenario: Listing movies
Given I have some movies stored
When I browse the list
Then I should see the complete collection
31. Amir Barylko - Better Cucumber Features
Leaky Scenarios
• Each scenario leaves the system in a
particular state
• The state has to be cleaned up for the next
scenario
• Otherwise it will “leak” into it
• One scenario should not depend on another
32. Amir Barylko - Better Cucumber Features
Generating data
• Use a framework to generate valid data
• FactoryGirl is a very good option
• FactoryGirl.create(:customer)
• FactoryGirl.create(:invalid_bank_accout)
• Faker will help you to generate fake data
33. Amir Barylko - Better Cucumber Features
Transforms
• Steps can have arguments
• Though regular expression they don’t always
show intent
• And also we may need to “reuse” them
34. Amir Barylko - Better Cucumber Features
Steps with arguments
Given /^I search for a movie “([^"]*)”$/ do |name|
.... # some code here
end
Given /^I have a movie called “([^"]*)”$/ do |name|
.... # some code here
end
35. Amir Barylko - Better Cucumber Features
Capture the argument
Given /^I search for a movie “(#{MOVIE_NAME})”$/ do |name|
.... # some code here
end
MOVIE_NAME = Transform /^([^"]+)$/ do | movie_name |
movie_name.downcase
end
36. Amir Barylko - Better Cucumber Features
Helpers
• Helpers are a great tool to encapsulate
common functionality
• Or to help describe better our intention
• and to avoid looking at ugly code
37. Amir Barylko - Better Cucumber Features
Current Instance
• The World is created for each scenario
• Instance variables have to be set
• Instead we can use a helper method
• to store/create the resource
38. Amir Barylko - Better Cucumber Features
Helper Class
module ProjectHelper
def current_project(project = nil)
@current_project ||= project
end
def project_list_page
@project_list_page ||= ProjectListPage.new
end
end
World(ProjectHelper)
39. Amir Barylko - Better Cucumber Features
Custom matchers
RSpec::Matchers.define :match_stored_projects do
match do |actual|
actual == Project.all.map { ... }
end
failure_message_for_should do |actual|
"The projects in the page should match...n" +
"The page contains #{actual}' n" +
"But the storage contains #{@expected}"
end
end
40. Amir Barylko - Better Cucumber Features
Page Objects
• The steps rely on the HTML implementation
• Searching for elements can be repetitive
• or ugly
• and not always show intention
41. Amir Barylko - Better Cucumber Features
What can we do?
Then /^I should see the complete list of projects$/ do
actual = all(:css, "#projects tbody tr")
• .map { |tr| tr.all("td").map(&:text) }
.map { |cells| ... }
expected = Project.all.map { |p| ... }
actual.should == expected
end
42. Amir Barylko - Better Cucumber Features
Abstraction!
class ProjectListPage
include PageObject
•
def projects
all(:css, "#projects tr").
drop(1). #drop the header
map { |r| r.all(:css, 'td').map(&:text) }.
map { |r| Project.new(...) }
end
end
43. Amir Barylko - Better Cucumber Features
Nicer steps
Then /^I should see the complete list of projects$/ do
projects_page.list.should == stored_projects
•
end
Page Object Helper
44. Amir Barylko - Better Cucumber Features
With a custom matcher
Then /^I should see the complete list of projects$/ do
projects_page.should list_stored_projects
•
end
Custom
Matcher
46. Amir Barylko - Better Cucumber Features
Next steps
• Focus your scenarios on “what” not “how”
• Read about scenario outlines
• Follow “the Cucumber book” practices
• Learn more about page objects pattern
• Start with a simple project
47. Amir Barylko - Better Cucumber Features
Resources
• Email: amir@barylko.com,
• Twitter: @abarylko
• Blog: http://orthocoders.com
• Website: http://maventhought.com