Rspec Stories – Keeping Steps Dry

30 Apr

When using Rspec stories you have plain text stories which we call the ‘story’ file and the ‘story steps’ file that maps the plain text story to programmatic code. Generally you end up with your story files not being DRY. This is not a worry, your stories are the domain specific languages detailing your acceptance/integration tests. Its like saying that your Rails Models are not DRY because they repeat lots of 'has_one'!

However within a single story file you can DRY things up a bit by reusing scenarios.

GivenScenario

We can use ‘GivenScenario: SCENARIO‘ within a scenario to call another scenario. Pretty much like a method call, it will run the called scenario and then return to continue with the original scenario.

  1. Story: As a Web admin
  2. I want to manages pages within an admin system
  3. So I can keep my blog up to date
  4.  
  5. Scenario: Add page
  6. ...
  7.  
  8. Scenario: edit page with valid details
  9. GivenScenario: Add page
  10. When I edit pages details
  11. Then it should save without errors


Drying The Story Steps…

More so than stories we should try and keep our story steps DRY. Duplicating ourself in steps makes it harder to maintain the tests and all the other horrors of breaking DRY. So here are some simple steps to help ensure that your step files stay nice and DRY.

Regular Expression Steps

To make your steps a little more flexible you can use relative expressions. Within your Given/When/Then functions rather than using a string parameter you use a regular expression:

  1. Then(/I will (not see|see) the admin toolbar/) do |see_or_not|
  2. if see_or_not == 'not see'
  3. response.should_not have_tag('div.adminToolbar')
  4. else
  5. response.should have_tag('div.adminToolbar')
  6. end
  7. end

Common Step files

Creating common step files which contain frequently reused Given/Then/Whens. I generally end up with a Selenium and a Webrat common step files since they are both core to driving my stories.

The common steps reside with the other step files:

  • steps/common_webrat_steps.rb
  • steps/common_selenium_steps.rb

Create the steps file as you would with any other rspec step file.

  1. steps_for(:common_webrat) do
  2.  
  3. When(/I visit my sites $page/) do |page|
  4. case page.downcase
  5. when 'homepage'
  6. visits '/'
  7. else
  8. visits page
  9. end
  10. end
  11.  
  12. When("I view the '$page' $type in my website") do |page,type|
  13. visits "/#{type.pluralize}/#{page}"
  14. end
  15.  
  16. When("I click $button") do |button|
  17. clicks_button button
  18. end
  19.  
  20. Then("I will be able to access the page in my website on url '$url'") do |url|
  21. visits url
  22. response.should be_success
  23. end
  24.  
  25. end

Then reference the common step file when you are creating the runner for your story:

  1. require File.dirname(__FILE__) + "/helper"
  2.  
  3. with_steps_for(:example, :common_webrat) do
  4. run File.join(File.dirname(__FILE__), '/example_story'), {:type => RailsStory}
  5. end

Helpers

Within your stories folder generated by rspec you have your lovely helper file:

stories/helper.rb

Within here you can create functions that are accessible within all steps.

You may start to find that your helper becomes a gigantic list of helper methods. Hence I tend to organize helpers into modules.

You can mix these helpers into Rspec stories so all steps can access them:

  1. module UserHelper
  2. def my_helper_method
  3. puts "Monkeys!"
  4. end
  5. end
  6.  
  7. module Spec::Story::World
  8. # Include helper modules here
  9. include UserHelper
  10. end
blog comments powered by Disqus