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.
Story: As a Web adminI want to manages pages within an admin systemSo I can keep my blog up to dateScenario: Add page...Scenario: edit page with valid detailsGivenScenario: Add pageWhen I edit pages detailsThen 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:
Then(/I will (not see|see) the admin toolbar/) do |see_or_not|if see_or_not == 'not see'response.should_not have_tag('div.adminToolbar')elseresponse.should have_tag('div.adminToolbar')endend
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.
steps_for(:common_webrat) doWhen(/I visit my sites $page/) do |page|case page.downcasewhen 'homepage'visits '/'elsevisits pageendendWhen("I view the '$page' $type in my website") do |page,type|visits "/#{type.pluralize}/#{page}"endWhen("I click $button") do |button|clicks_button buttonendThen("I will be able to access the page in my website on url '$url'") do |url|visits urlresponse.should be_successendend
Then reference the common step file when you are creating the runner for your story:
require File.dirname(__FILE__) + "/helper"with_steps_for(:example, :common_webrat) dorun File.join(File.dirname(__FILE__), '/example_story'), {:type => RailsStory}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:
module UserHelperdef my_helper_methodputs "Monkeys!"endendmodule Spec::Story::World# Include helper modules hereinclude UserHelperend