Tonight, I got a chance to give a presentation to the Columbus Ruby Brigade on MetricFu. If you’re interested in seeing it, here is the PDF.
As a recent convert to Ruby on Rails, I’ve fallen in love with the combination of Cucumber and its associated ecosystem (RSpec, webrat, etc.). When you first start a Cuke project, it provides an initial set of helpful steps in a file called webrat_steps.rb in your features/step_definitions file. While these are helpful, I found myself creating several other steps that seemed to be more generally useful than just my project. I’ve packaged these steps up into a gem called cukesteps that you can get at:
These steps fall into three basic categories:
- Object creation step(s)
- Debugging steps
- Semantic markup checking steps
Object creation steps
In our project, we have fairly complex sets of ActiveRecord models and associations. Trying to do acceptance testing proved challenging because several tests require non-trivial amounts of setup to work. Consider for an example an application that helps you find restaurants with foods that enable you to lose weight. In this kind of application, you might have models for Food, Restaurant, Brand, and Location. And you need to tie these together in your features as in this example:
Given the following foods exist | food | calories | fat_calories | | eggs | 200 | 180 | | burger | 350 | 200 | | salad | 100 | 80 | And the following restaurants exist | brand | location | foods | | mcdonalds | chicago il | eggs, burger | | panera | chicago il | eggs, salad | | mcdonalds | dallas tx | burger, salad | When I fill in "city" with "chicago" And I select "under 100" from "calories" And I press "search" Then I should see "panera"
Creation of restaurants in this example is fairly complicated (and maybe over-complicated):
– the base Restaurant object is probably created with FixtureReplacement, FactoryGirl, Object Daddy, Fixjour, or something similar, but:
– the associated brand might have to be grabbed using Brand.find_by_name to make use of a db:seed kind of table
– the associated location could be created with whatever Factory mechanism you’re using with only a single string param being enough to make it interesting
– the associated foods come from objects created in the prior step
Getting these steps with the cukesteps gem installed is a matter of doing the following in your features/support/env.rb file for cuke:
require 'cukesteps' include CukeAssociationHelpers cuke_association_attributes(:brand => :name, :location => :address)
The use of cuke_association_attributes causes the brand and location columns in the second step to be treated special. Instead of being treated as simple attributes, they instead are found or built. If a result of find_by_attribute_name (e.g. find_by_name for brand or find_by_address for location) returns non-nil, then that object is used as the associated object. Otherwise, the object is created using a create_model-style factory method. The foods column is also special in that it refers back to the Food objects created in the prior step.
The cukesteps gem provides four basic debugging steps:
- Then I debug
- Then save_and_open_page
- Then n foos should exist
- Then dump all foos to standard output
These steps are not ones you’d leave in your feature file for any length of time, but they can come in handy for chasing down problems you encounter along the way.
Semantic markup checking steps
These steps help encourage use of semantic IDs and Class Names in your markup. The use of the articles “a” and “the” represent a class and ID respectively. For example, “I should see a photo in the search-results” ensures that the the page contains an element of class photo inside an element of ID search-results. The steps available here are:
- Then I should see the foo (e.g. Then I should see the search-results)
- Then I should see a|an foo (e.g. Then I should see a listing)
- Then I should see a foo in the bar (e.g. Then I should see a listing in the search-results)
- Then I should see the foo in the bar (e.g. Then I should see the map in the search-results)
- Then I should see n foos in the bar (e.g. Then I should see 4 listings in the search-results)
- Then I should see n to m foos in the bar (e.g. Then I should see 3 to 5 listings in the search-results)
- Then the baz in the bar should contain a foo (e.g. Then the map in the search-results should contain a resizer)
The negation of each of the above is also available (e.g. Then I should not see a listing in the search-results)
To use this gem, just do the following:
gem sources github.com gem install mdoel-cukesteps
Feel free to fork away and send me feedback.