dmitry sharkov - maturing your cucumber suites
TRANSCRIPT
A UNIVERSAL PROBLEM WITH CODE
from http://www.geardiary.com/2010/09/17/random-cool-stuff-too-many-cats-here-are-a-few-ways-to-organize-them/
It often starts clean, maintainable, and reasonably well thought out.
A UNIVERSAL PROBLEM WITH CODE
from http://www.examiner.com/article/hoarding-a-misunderstood-disorder
Keeping it that way is hard.
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 BADScenario: Successfully submitting payment Given I visit the landing page When I enter my username And I enter my password And I press the login button Then I am authenticated And I see the make a payment link When I click on the make a payment link Then I am on the account details page When I select the pay current balance button And I select today from the payment date calendar And I press the pay now link Then I am on the checkout page When I select credit card from the payment options Then the credit card details dialog appears When I enter my credit card number And I select the expiration date And I enter my cvv number . . .
a million stepscompletely unfocused
not business centric
AVOIDING BAD SCENARIOS
In the first place, DO NOT write steps to be atomic application interactions.
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" Given I am an administrator
Given /^I am an administrator$/ do" visit(Home).login_with(user,pass)"end
Don't act as a mouse and keyboard. !Be declarative.
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
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
Suppose we have a website and Cucumber scenarios to test the following: logging in, updating profile, resetting password, registering a new account.
HOOKS GONE BAD
@registration!@authentication!@updateProfile!@passwordReset!@mobileRegistration!@mobileAuthentication
Things get more complex as the feature set of the application grows. Adding mobile: Adding Facebook integration:
@facebookRegistration @facebookAuthentication!@facebook
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"end!!# need a real email to send password to"Before('@resetPassword,@emailVerification,@updateEmail') do" set_users_email_to_a_known_test_account"end!!# if not regular desktop user then some account fields are empty"Before('@mobileUpdate,@facebookUpdate','~@update') do" clear_fields_for_non_regular_account"end!!Before('@facebookAuthentication,@facebookRegistration') do" remove_password_and_add_facebook_id"end!!Before('@facebook','~@facebookRegistration') do" create_user_with_facebook_specific_fields"end!...
HOOKS HAVE GONE ROGUE • Preconditions become increasingly unclear. !
• Hook interaction becomes difficult to grasp. !
• Tag listings get freaky AND/OR/NOT logic.!
• Feature files don't tell the whole story. !
• Way too many hooks!
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 ...
@registration @emailVerification Feature: verification of Scenario: successful ver When I ...
@authentication @facebookAuthentication @facebookUser @existingUser Feature: authentication f Scenario: successful logi When I ...
@registration @emailVerification @nonrandomUser Feature: verification of Scenario: successful ver When I ...
BEFORE
AFTER
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 userBefore('@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 '[email protected]' subject 'yo' end @email.deliver! end 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 '[email protected]'
Not very DRY…
Not very DRY…
FIXING BAD STEPS
Extract common code into helper methods
module EmailHelpers def send_email (text) @email = Mail.new do body text from @user.email to '[email protected]' subject 'yo' end @email.deliver! end end World(EmailHelpers)features/support/email_helpers.rb
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
FIXING BAD STEPS
Rework regexes to reduce the number of definitionsGiven /^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') endGiven /^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
• 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/>
gmail.com rendered HTML
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.
QUESTIONS & [email protected] @DmitrySharkov
from http://twistedsifter.com/2013/08/surreal-landscapes-made-from-food-carl-warner/