= pickle
Pickle gives you cucumber steps that create your models easily from factory-girl or
machinist factories/blueprints. You can also just use ActiveRecord as a factory but it's not as cool.
Pickle can make use of different ORMs for finding records. Currently ActiveRecord and DataMapper adapters are
provided. More adapters welcome!
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
== Quickstart
This is a quickstart guide for rails apps. Firstly, install {cucumber-rails}[http://github.com/aslakhellesoy/cucumber-rails], and its dependencies.
Then do the following:
=== Rails 3:
Add the gem to your Gemfile:
gem 'pickle'
Then install with:
bundle install
Discover the options for the generator:
rails g pickle --help
Run the generator, e.g:
rails g pickle --paths --email
=== For Rails 2:
Add the following to config/environments/cucumber:
config.gem 'pickle'
Install the gem with
rake gems:install RAILS_ENV=cucumber
Run the generator with:
script/generate pickle [paths] [email]
== Resources
Github for code: http://github.com/ianwhite/pickle
Gemcutter for the gem: http://gemcutter.org/gems/pickle
Rdoc.info for docs: http://rdoc.info/projects/ianwhite/pickle
Google group for questions: http://groups.google.com/group/pickle-cucumber
Lighthouse for bugs: http://ianwhite.lighthouseapp.com/projects/25941-pickle
Railscast presentation: http://railscasts.com/episodes/186-pickle-with-cucumber
Blog articles: {dynamic50: Integration testing with cucumber and pickle}[http://blog.dynamic50.com/index.php/2009/04/integration-testing-with-cucumber-and-pickle/], {rubyflare: pickle my cucumber}[http://rubyflare.com/2009/10/28/pickle-my-cucumber/]
== Using Pickle
Now have a look at features/step_definitions/pickle_steps.rb
If you want path steps and email steps then just add the 'paths' and/or 'email' options to the generator.
The code/steps will be written to features/env/paths.rb and
features/step_definitions/email_steps.rb respectively.
=== Using with plain ole Active Record or DataMapper
Pickle comes with adapters for Active Record and DataMapper.
If you have an AR/DM 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"
=== Using with factory-girl or machinist
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
==== Machinist: require your blueprints and reset Shams
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
==== FactoryGirl: make sure factories are loaded
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'
=== Using with an ORM other than ActiveRecord or DataMapper
Pickle can be used with any modelling library provided there is an adapter written for it.
Adapters are very simple and exist a module or class with the name "PickleAdapter" available to the class. For example
User.const_get(:PickleAdapter) #=> should return a pickle adapter
The Active Record and DataMapper ones can be found at
ActiveRecord::Base::PickleAdapter and DataMapper::Resource::PickleAdapter respectively.
See how to implement one by looking at the ones provided in the pickle source in lib/pickle/adapters/*
=== Configuring Pickle
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).
== API
=== Steps
When you run script/generate pickle you get the following steps
==== Given 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 |
===== Named machinist blueprints
"Given a named model exists with fields"
The latest version of pickle supports {named machinist blueprints}[http://github.com/notahat/machinist/commit/d6492e6927a8aa1819926e48b22377171fd20496].
If you had the following blueprints:
User.blueprint do
name
email
end
User.blueprint(:super_admin) do
role { "admin" }
end
User.blueprint(:activated) do
activated { true }
end
You could create a user with pickle by simply adding the name of the blueprint before the model:
Given a super admin user exists
And an activated user exists with name: "Fred"
This is much nicer than having to set up common configurations in your steps all the time, and far more readable to boot.
==== Then steps
===== Asserting existence of models
"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 |
===== Asserting associations
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
===== Asserting predicate methods
"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
=== Regexps for use in your own steps
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
== Run the tests
To run the specs do:
rake spec
To run the features (rails 2.3 only ATM):
rake cucumber
== Contributors
The following people have made Pickle better:
* Brian Rose & Kevin Olsen
* {Christopher Darroch}[http://github.com/chrisdarroch]
* {Szymon Nowak}[http://github.com/szimek]
* {H.J. Blok}[http://github.com/hjblok]
* {Daniel Neighman}[http://github.com/hassox]
* {Josh Bassett}[http://github.com/nullobject]
* {Nick Rutherford}[http://github.com/nruth]
* {Tobi Knaup}[http://github.com/guenter]
* {Michael MacDonald}[http://github.com/schlick]
* {Michael Moen}[http://github.com/UnderpantsGnome]
* {Myron Marston}[http://github.com/myronmarston]
* {Stephan Hagemann}[http://github.com/xing]
* {Chris Flipse}[http://github.com/cflipse]