Skip to main content
MATURING YOUR
CUCUMBER TEST SUITE
Dmitry “D-Shark” Sharkov
dsharkov@gmail.com

@DmitrySharkov
A UNIVERSAL PROBLEM WITH CODE
It often starts clean, maintainable, and reasonably well thought out.

from http://www.geardiary.com/2010/09/17/random-cool-stuff-too-many-cats-here-are-a-few-ways-to-organize-them/
A UNIVERSAL PROBLEM WITH CODE
Keeping it that way is hard.

from http://www.examiner.com/article/hoarding-a-misunderstood-disorder
Test Suites Are
Not Immune
Copy-pasting
Overly complex logic
Inconsistent architecture
Rubbish naming
Brittle design
(Technical debt)
from http://www.villageharvestrice.com/2012/08/cucumber-avocado-sushi-rolls/
In the worst cases, our code can get so
cumbersome that working on it stops being fun.

We should take steps to
avoid such cases. We can
revisit, refactor, and look
ahead.

from http://nekozushi.shop-pro.jp/
Let's look at some ways your Cucumber can start to look
like… something else… and how that can be avoided.

from http://laughingsquid.com/how-to-make-a-cucumber-look-like-a-living-slithering-snake/

http://cakeheadlovesevil.wordpress.com/2010/03/16/skeleton-cucumber/
SCENARIOS GONE BAD
Scenario:
Given
When
And
And
Then
And
When
Then
When
And
And
Then
When
Then
When
And
And
. .

Successfully submitting payment

I visit the landing page

I enter my username

I enter my password

I press the login button

I am authenticated

I see the make a payment link

I click on the make a payment link

I am on the account details page

I select the pay current balance button

I select today from the payment date calendar

I press the pay now link

I am on the checkout page

I select credit card from the payment options

the credit card details dialog appears

I enter my credit card number

I select the expiration date

I enter my cvv number

.

a million steps
completely unfocused
not business centric
AVOIDING BAD SCENARIOS
In the first place, DO NOT write steps to be atomic
application interactions.
Given
And
And
And
And

I
I
I
I
I

click the login button

enter my username

enter my password

press enter

see "Welcome, admin"




Given I am an administrator

Don't act as a
mouse and
keyboard.
!

Be declarative.

Given /^I am an administrator$/ do"
visit(Home).login_with(user,pass)"
end
FIXING BAD SCENARIOS
As a first pass, abstract away the offending steps.
Given /^I am an administrator$/ do

steps %q{

Given I click the login button

And I enter my username

And I enter my password

And I press enter

And I see "Welcome, admin"

}

end

Then as a second pass, refactor the step definition.
HOOKS GONE BAD

from http://www.hdwpapers.com/captain_hook_hd_desktop_wallpaper-wallpapers.html
HOOKS GONE BAD
Suppose we have a website and Cucumber scenarios to test the
following: logging in, updating profile, resetting password,
registering a new account.
Tagging these scenarios for
traceability and isolation yields
@registration!
@authentication!
@updateProfile!
@passwordReset
from http://mysalvagedtreasures.blogspot.com/2010/08/yardstick-makeover-and-yard-sale-finds.html
HOOKS GONE BAD
Things get more complex as the feature set of the application
Adding Facebook integration:
grows. Adding mobile:
@facebookRegistration

@registration!
@authentication!

@facebookAuthentication!

@updateProfile!

@facebook

@passwordReset!
@mobileRegistration!
@mobileAuthentication
Seems reasonable. But look at what can happen to our hooks…
# trying to avoid collisions with other people running tests"
Before('@authentication,@facebookAuthentication,"
@update,@facebookUpdate') do"
create_user_with_random_id_and_fake_email"
Preconditions become increasingly unclear.
end!

HOOKS HAVE GONE ROGUE

•

!

!

# need a real email to send password to"
Hook interaction becomes difficult to grasp.
Before('@resetPassword,@emailVerification,@updateEmail') do"
set_users_email_to_a_known_test_account"
!
end!

•

!

• Tag listings get freaky AND/OR/NOT logic.


# if not regular desktop user then some account fields are empty"
!
Before('@mobileUpdate,@facebookUpdate','~@update') do"
clear_fields_for_non_regular_account"
Feature files don't tell the whole story.
end!

•

!

!

Before('@facebookAuthentication,@facebookRegistration') do"
Way too many hooks!
remove_password_and_add_facebook_id"
end!

•

!
Before('@facebook','~@facebookRegistration') do"
create_user_with_facebook_specific_fields"
end!
...
AVOIDING BAD HOOKS
Flip the problem on its head.
Don't figure out what each
scenario needs in hooks.rb
Let each scenario TELL you
what it needs.

from http://dessertating.wordpress.com/2013/08/13/in-the-words-of-gob-bluth-you-dont-have-time-for-my-illusions/
AVOIDING BAD HOOKS
@authentication

@facebookAuthentication

Feature: authentication f
Scenario: successful logi

When I ...

@authentication

@facebookAuthentication

@facebookUser

@existingUser

Feature: authentication f
Scenario: successful logi

When I ...

BEFORE

@registration



@emailVerification

Feature: verification of
Scenario: successful ver

When I ...




AFTER

@registration

@emailVerification

@nonrandomUser



Feature: verification of
Scenario: successful ver

When I ...



Before('@randomUser') do

@test_user_data[email] = create_random_example_email

end



# need a real email to send stuff to

Before('@nonrandomUser') do

@test_user_data[email] = known_test_email_account

end



Before('@facebookUser') do

@test_user_data[email] = known_fb_account_email

@test_user_data[facebook_id] = known_fb_account_id

@test_user_data[password] = nil

end



# for all scenarios that require an existing user

Before('@existingUser') do

create_user(@test_user_data)

end
AVOIDING BAD HOOKS
With the old approach, adding
Google+ integration would have led
to adding a new hook and modifying
several existing hooks to include or
exclude new tags.
With the new approach, all you need
is the new hook and these tags for
your Google+ scenarios.

@googleUser

@existingUser
from http://www.dailymail.co.uk/tvshowbiz/article-2024541
AVOIDING BAD HOOKS
OR…
You can use a Background in
the first place and have steps
replace the hooks.

Background: google+ user

Given I am a Google+ user

And I have an existing account
from http://disney.wikia.com/wiki/Peter_Pan_(character)
STEPS GONE BAD
Given /^I send a nice email to my colleague$/ do

@email = Mail.new do

body 'Let us partake in brewed beverages after work'

from @user.email

to 'bob@example.com'

subject 'yo'

end

@email.deliver!

end


Not very DRY…




Not very DRY…

Given /^I send an angry email to my colleague$/ do

@email = Mail.new do

body 'I have had it with your shenanigans'

from @user.email

to 'bob@example.com'
FIXING BAD STEPS
Extract common code into helper methods
Given /^I send a nice email to my c

send_email('Let us partake in bre

end



Given /^I send an angry email to my

send_email('I have had it with yo

end

features/support/email_helpers.rb

module EmailHelpers

def send_email (text)

@email = Mail.new do

body text

from @user.email

to 'bob@example.com'

subject 'yo'

end

@email.deliver!

end

end

World(EmailHelpers)
FIXING BAD STEPS
Rework regexes to reduce the number of definitions
Given /^I send a nice email to my colleague$/ do

send_email('Let us partake in brewed beverages')

end



Given /^I send an angry email to my colleague$/ do

send_email('I have had it with your shenanigans')

end
Given /^I send an? (nice|angry) email to my colleague$/ do |style|

body = style == 'nice' ? 'Nice code bro' : 'You disgust me'

send_email(body)

end
STEPS GONE BAD
Given /^I (?:click|press) (on )?the (.+) (button|link)$/ do |

. . .

end



Given /^I enter "(.+)" (?:in|into) the (.+) $/ do |text, elem

. . .

end



Given /^I see "(.+)"$/ do |text_on_page|

. . .

end

DRY! However…

• Unnecessary layer of abstraction
• Everyone has to learn this 'DSL'
• Everyone touches this code
AVOIDING BAD STEPS
Steps should be domain-focused.
Given /^I try to reset my password$/ do"
# Here put ruby code. watir-webdriver, capybara, page-object."
# You have to know one of them anyway."
end

Steps should be feature-focused.
This means fewer people have to touch the same steps.

Testers don't have to keep many available steps in mind.
If Feature files are not used by the business, then you don't need
Feature files at all. You can forego the gherkin grammar and
write tests directly with ruby.
INTERACTION GONE BAD
gmail.com rendered HTML

• Pages have lots of elements
in common and page
objects get repetitive

• Some steps are brutal to get
done through a web driver
altogether (reading email)

• Some pages are fancy and
have custom tags such as
<fb:registration/>
FIXING BAD
INTERACTION
Abstract away common elements on pages and use inheritance
to bring them in.
require_relative 'base_page_with_navigation'"
!
class FacebookRegistrationPage < BasePageWithNavigation"
page_url(BASE_URL+'/fbregistration')"
"
text_field(:first_name, :name => 'firstName')"
text_field(:last_name, :name => 'lastName')"
. . .
AVOIDING BAD
INTERACTION
Go in a different direction when webpages or other
clients under test are too nasty.

http://www.veritaslendingsolutions.com/Solutions.aspx
AVOIDING BAD
INTERACTION
Use abstraction for pieces of interactivity besides just screens.
class AccountVerificationEmail"
attr_accessor :verification_link!

!
def initialize (browser,text)"
@browser = browser"
html = Nokogiri::HTML(sanitize_email_text(text))"
verification_links = html('.//a[contains(@href,"verifyAcct")]')"
@verification_link = verification_links[0]['href']"
end"

!
def click_on_verification_link"
if @verification_link != nil then"
@browser.goto(@verification_link)"
end"
end"
end
AVOIDING BAD
INTERACTION
Don't forget — you are limited by any technology.
Email doesn't have to be read through a webpage. Step
definitions can delegate complex logic to pages objects.
There is always a way to make something difficult a
little bit easier.
from http://twistedsifter.com/2013/08/surreal-landscapes-made-from-food-carl-warner/

QUESTIONS & COMMENTS
dsharkov@gmail.com @DmitrySharkov