You were probably hoping for some Rosey the Riveter poster…
Instead, I am going to extend my small MongoMapper example to include Factory Girl. The steps are pretty simple:
- Go here to install…
- Create your factories
- Use the factories in Cucumber/RSpec
Factory Construction
I created a new “factories” folder under the spec folder:
The factories for User and Event are quite simple:
Factory.define :user do |u| u.name ('a'..'z').to_a.shuffle[0..7].join.capitalize end
and
require 'factory_girl' def dummy_word(len=6) ('a'..'z').to_a.shuffle[0..len].join.capitalize end def dummy_date secs_in_day = 24*60*60 Time.now + (rand(60)*secs_in_day - 30) end Factory.define :event do |e| e.title "#{dummy_word} #{dummy_word 3} #{dummy_word 10}" e.date dummy_date end
Refactor Original Setup
Instead of using this style of test data creation:
@event = Event.create(:title => "Code Retreat Timbuktoo", :user => @fred)
We will use the new factory as follows:
@event = Factory(:event, :title => "Code Retreat Timbuktoo", :user => @fred)
Refactor Cucumber
The given went from this:
Given /^A set of events$/ do fred = User.find_or_create_by_name("fred") (1..10).each do Event.create(:title=>"#{dummy_word} #{dummy_word 3} #{dummy_word 10}", :date => dummy_date, :user => fred) end harry = User.find_or_create_by_name("harry") (1..10).each do Event.create(:title=>"#{dummy_word} #{dummy_word 3} #{dummy_word 10}", :date => dummy_date, :user => harry) end Event.count.should == 20 end
to this – including refactoring out dummy_title, and reducing it to one loop:
Given /^A set of events$/ do fred = User.find_or_create_by_name("fred") harry = User.find_or_create_by_name("harry") (1..10).each do evt = Factory(:event, :title => dummy_title, :date => dummy_date, :user => fred) evt = Factory(:event, :title => dummy_title, :date => dummy_date, :user => harry) end Event.count.should == 20 end
Subtle Details
The beauty of having tests is that I could easily mess around with getting some of the Factory Girl configuration stuff in the right place. Try something, run the test, adjust as needed until all are back to green.
The file features/support/env.rb got some additions so that Cucumber could find the factories:
$LOAD_PATH << File.expand_path('../../../app/model' , __FILE__) require 'user' require 'event' require 'spec/factories/events.rb' require 'spec/factories/users.rb' load 'config/mongo_db.rb'
All the tests still pass!
More Complicated Example
For a project I work on, my factories look like this, with auto-creation of random IDs:
def random_months(months) day_in_secs = (24*60*60) (1+rand(months))*30*day_in_secs end # ----------- GROUP ----------- Factory.sequence :group_num do |n| "99#{n}#{rand(n)}" end Factory.define :group do |g| g.group_num {Factory.next(:group_num)} g.name "Greatest Group" end # ----------- ACCOUNT ----------- Factory.sequence :doctor_num do |n| "999992#{n}#{rand(200+n)}" end Factory.sequence :login do |n| "AB#{rand(n*68)}bx#{rand(200+n)}" end Factory.sequence :msid do |n| "CQ987Z12#{n}#{rand(n)}" end Factory.define :account do |a| pw = 'password' a.msid { Factory.next(:msid) } a.doctor_num { Factory.next(:doctor_num) } a.first_name "James" a.last_name "Jones" a.role 'user' a.password pw a.password_confirmation pw a.email Setting.get("AutoEmail") a.login { Factory.next(:login) } end # ----------- PATIENT ----------- Factory.sequence :patient_num do |n| "#{n}#{rand(300+n)}" end Factory.define :patient do |pt| # pt.patient_num "10000009" pt.patient_num {Factory.next(:patient_num)} pt.emr_num "1853286" pt.first_name "John" pt.last_name "Johnson" pt.dob {(Time.now - random_months(36))} pt.count_public_encounters 1 pt.count_public_events 2 end