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

