Pickle gives you cucumber steps that create your models easily from factory-girl or machinist factories/blueprints. You can also just use ActiveRecord but it’s not as cool.
References to the models are stored in the current world, not necessarily for the purpose of checking the db (although you could use it for that), but for enabling easy reference to urls, and for building complex givens which require a bunch of models collaborating
Github for code: github.com/ianwhite/pickle
Gemcutter for the gem: gemcutter.org/gems/pickle
API docs: Pickle rdoc on github
Google group for questions: groups.google.com/group/pickle-cucumber
Lighthouse for bugs: ianwhite.lighthouseapp.com/projects/25941-pickle
Railscast presentation: railscasts.com/episodes/186-pickle-with-cucumber
Blog articles: dynamic50: Integration testing with cucumber and pickle, rubyflare: pickle my cucumber
Install pickle either as a rails plugin, or a gem
# gem from gemcutter sudo gem install pickle # gem dependency (in config/environments/cucumber.rb) config.gem 'pickle' # plugin script/plugin install git://github.com/ianwhite/pickle.git # or, plugin as submodule git submodule add git://github.com/ianwhite/pickle.git vendor/plugins/pickle
It’s tested against all stable branches of 2.x rails, and edge, with the latest versions of rspec, cucumber, factory_girl, machinist.
To run the specs do:
rake spec
To run the features (rails 2.3 only ATM):
rake cucumber
The following people have made Pickle better:
(you’d better install cucumber)
script/generate pickle [paths] [email]
Now have a look at features/step_definitions/pickle_steps.rb
If you want path steps and email steps then just add ‘paths’ and/or ‘email’. The code/steps will be written to features/env/paths.rb
and features/step_definitions/email_steps.rb
respectively.
If you have an AR called ‘Post’, with required fields ‘title’, and ‘body’, then you can now write steps like this
Given a post exists with title: "My Post", body: "My body"
But you’re using Machinist or FactoryGirl right?! To leverage all of the factories/blueprints you’ve written, you can just do stuff like
Given a user exists And another user exists with role: "admin" # later Then a user should exist with name: "Fred" And that user should be activated # this uses rspec predicate matchers
(The latest version of pickle supports multiple blueprints, for earlier versions of machinist use pickle <= 0.1.10)
In your features/support/env.rb
add the following lines at the bottom
require "#{Rails.root}/spec/blueprints" # or wherever they live Before { Sham.reset } # reset Shams in between scenarios
In your config/environments/cucumber.rb file, make sure the factory-girl gem is included (unless it’s installed as a plugin).
If that doesn’t solve loading issues then require your factories.rb file directly in a file called ‘features/support/factory_girl.rb’
# example features/support/factory_girl.rb require File.dirname(__FILE__) + '/../../spec/factories'
You can tell pickle to use another factory adapter (see Pickle::Adapter), or create mappings from english expressions to pickle model names. You can also override many of the options on the Pickle::Config object if you so choose.
In: features/support/pickle.rb
require 'pickle/world' Pickle.configure do |config| config.adapters = [:machinist, YourOwnAdapterClass] config.map 'me', 'myself', 'my', 'I', :to => 'user: "me"' end
Out of the box pickle looks for machinist, then factory-girl, then finally active-record ‘factories’. If you find that your steps aren’t working with your factories, it’s probably the case that your factory setup is not being included in your cucumber environment (see comments above regarding machinist and factory-girl).
When you run script/generate pickle
you get the following steps
“Given a model exists”, e.g.
Given a user exists Given a user: "fred" exists Given the user exists
“Given a model exists with fields”, e.g.
Given a user exists with name: "Fred" Given a user exists with name: "Fred", activated: false
You can refer to other models in the fields
Given a user exists And a post exists with author: the user Given a person: "fred" exists And a person: "ethel" exists And a fatherhood exists with parent: user "fred", child: user "ethel"
“Given n models exist”, e.g.
Given 10 users exist
“Given n models exist with fields”, examples:
Given 10 users exist with activated: false
“Given the following models exist:”, examples:
Given the following users exist | name | activated | | Fred | false | | Ethel | true |
“Then a model should exist”, e.g.
Then a user should exist
“Then a model should exist with fields”, e.g.
Then a user: "fred" should exist with name: "Fred" # we can label the found user for later use
You can use other models, booleans, numerics, and strings as fields
Then a person should exist with child: person "ethel" Then a user should exist with activated: false Then a user should exist with activated: true, email: "fred@gmail.com"
“Then n models should exist”, e.g.
Then 10 events should exist
“Then n models should exist with fields”, e.g.
Then 2 people should exist with father: person "fred"
“Then the following models exist”. This allows the creation of multiple models using a table syntax. Using a column with the singularized name of the model creates a referenceable model. E.g.
Then the following users exist: | name | activated | | Freddy | false | Then the following users exist: | user | name | activated | | Fred | Freddy | false |
One-to-one assocs: “Then a model should be other model’s association”, e.g.
Then the person: "fred" should be person: "ethel"'s father
Many-to-one assocs: “Then a model should be [in|one of] other model’s association”, e.g.
Then the person: "ethel" should be one of person: "fred"'s children Then the comment should be in the post's comments
“Then a model should [be|have] [a|an] predicate”, e.g.
Then the user should have a status # => user.status.should be_present Then the user should have a stale password # => user.should have_stale_password Then the car: "batmobile" should be fast # => car.should be_fast
“Then a model should not [be|have] [a|an] predicate”, e.g.
Then person: "fred" should not be childless # => fred.should_not be_childless
By default you get some regexps available in the main namespace for use in creating your own steps: ‘capture_model`, `capture_fields`, and others (see lib/pickle.rb)
(You can use any of the regexps that Pickle uses by using the Pickle.parser namespace, see Pickle::Parser::Matchers for the methods available)
capture_model
Given /^#{capture_model} exists$/ do |model_name| model(model_name).should_not == nil end Then /^I should be at the (.*?) page$/ |page| if page =~ /#{capture_model}'s/ url_for(model($1)) else # ... end end Then /^#{capture_model} should be one of #{capture_model}'s posts$/ do |post, forum| post = model!(post) forum = model!(forum) forum.posts.should include(post) end
capture_fields
This is useful for setting attributes, and knows about pickle model names so that you can build up composite objects with ease
Given /^#{capture_model} exists with #{capture_fields}$/ do |model_name, fields| create_model(model_name, fields) end # example of use Given a user exists And a post exists with author: the user # this step will assign the above user as :author on the post