Factory Grabber
Factory grabber speeds up your tests by grabbing the nearest appropriate database records to suit your needs.
The idea is simple: most of the time, when using factories, you don't really care about the specific attributes, you just need a database record to play with. factory_grabber will 'grab' a matching record from the database if available or create any extra records if required. Less inserts to the database means faster tests.
At the moment, only "factory_girl":http://github.com/thoughtbot/factory_girl by thoughtbot is supported. If you'd like to see more factories supported please let me know ([email protected])
To install:
As a gem:
sudo gem install git://github.com/GavinM/factory_grabber.git
# environment.rb
config.gem "thoughtbot-factory_girl", :lib => "factory_girl"
config.gem "factory_grbber", :version => ">=1.0.2", :lib => false
As a plugin:
script/plugin install git://github.com/GavinM/factory_grabber.git
Important:
To benefit from this gem, set use_transactional_fixtures=() to false in spec_helper.rb
Spec::Runner.configure do |config|
config.use_transactional_fixtures = false
config.use_instantiated_fixtures = false
config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
end
Example Usage
First, add the following line to spec_helper.rb or test_helper.rb:
require "factory_grabber"
Then, in your tests/specs:
# to grab 1 comment:
@comment = Grab.a_comment
# to grab 1 article:
@article = Grab.an_article
#to grab 4 users named "John Smith":
@users = Grab.four_users(:first_name => "John", :last_name => "Smith")
#to grab 73 users with standard factory attributes:
@users = Grab.seventy_three_users
Practical examples:
In a controller spec... describe "GET /posts/1" do
integrate_views
before do
# will create a record with these attributes if required, if not it will find the existing record
@post = Grab.one_post :title => "This is my first post", :body => "This is the post body"
end
def do_get
get :show, :id => @post
end
it "should show the post title" do
do_get
response.should include_text(/This is my first post/)
end
it "should show the post body" do
do_get
response.should include_text(/This is the post body/)
end
end
describe "GET /posts?page=1" do
# testing pagination
integrate_views
before do
# ensures there are at least eleven Post records
# if there are less than eleven, new posts are created
# if there are eleven or more no posts are created
Grab.eleven_posts
end
def do_get
get :index, :page => 1
end
it "should find the latest 10 posts" do
do_get
assigns[:posts].should == Post.find(:all, :order => "created_at DESC", :limit => 10)
end
end
At the moment, all numbers between one and ninety_nine are supported. The general syntax for Grab methods is:
Grab.[number_in_words]_[factory_name]
Performance gains
Here's a quick example of the possible performance gains:
Rehearsal --------------------------------------------------------------
Create 50 new factories 0.130000 0.200000 0.330000 ( 6.785332)
Grab 50 separate factories 0.320000 0.020000 0.340000 ( 0.332814)
Grab 50 factories at once 0.010000 0.000000 0.010000 ( 0.012414)
----------------------------------------------------- total: 0.680000sec
user system total real
Create 50 new factories 0.100000 0.200000 0.300000 ( 6.354282)
Grab 50 separate factories 0.300000 0.000000 0.300000 ( 0.310373)
Grab 50 factories at once 0.020000 0.000000 0.020000 ( 0.011400)
(These results can be produced for your own environment by running the file "performance_test.rb" in factory_grabber/lib/spec/performance_test.rb)
Known Issues
At the moment, existing factories are found calling the model name. If your factory names do not match the name of the models then new factories will be created each time. eg.
Factory(:admin_user, :class => :user) do |f|
f.username("admin_user")
f.admin(true)
# ... etc ...
end
Grab.an_admin_user # => will not find records from the database.
Feedback
This project is still an infant - if the Ruby community find it useful I plan on adding a lot more. Please send any ideas/feedback to [email protected]
Free to upload, edit, share, fork etc.
Gavin Morrice
www.handyrailstips.com