Nov 23
Factory Girl now has callbacks thanks to Nate Sutton (a.k.a. fowlduck).

There are three callbacks respectively called after Factory methods you already know:
| after_build | Factory.build |
| after_create | Factory.create |
| after_stub | Factory.stub |
These come in handy in a number of common use cases.
Basic has many associations
Models:
class Article < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :article
end
Factories:
Factory.define :article do |article|
article.body { "password" }
end
Factory.define :comment do |comment|
comment.body { 'Great article!' }
end
Factory.define :article_with_comment, :parent => :article do |article|
article.after_create { |a| Factory(:comment, :article => a) }
end
Nice. Callbacks let us do this:
article = Factory(:article_with_comment)
Instead of this:
article = Factory(:article)
comment = Factory(:comment, :article => article)
Polymorphic relationships
The savings get larger when the object graph gets more complex:
Models:
class User < ActiveRecord::Base
has_many :interests, :as => :interested
has_many :topics, :through => :interests
end
class Interest < ActiveRecord::Base
belongs_to :topic
belongs_to :interested, :polymorphic => true
end
Building block factories:
Factory.define :user do |user|
user.email { Factory.next(:email) }
user.password { "password" }
user.password_confirmation { "password" }
end
Factory.define :email_confirmed_user, :parent => :user do |user|
user.email_confirmed { true }
end
Factory.define :topic do |topic|
topic.name {'topic_name'}
end
Factory.define :interest do |interest|
interest.association(:topic)
interest.interested { |i| i.association(:user) }
end
Factory.define :music_interest, :class => 'Interest' do |interest|
interest.topic { |topic| topic.association(:topic, :name => "Music") }
end
Factory.define :sports_interest, :class => 'Interest' do |interest|
interest.topic { |topic| topic.association(:topic, :name => "Sports") }
end
And now the payoff:
Factory.define :musical_user, :parent => :email_confirmed_user do |user|
user.after_create { |u| Factory(:music_interest, :interested => u) }
end
Factory.define :sporty_user, :parent => :email_confirmed_user do |user|
user.after_create { |u| Factory(:sports_interest, :interested => u) }
end
Again, factories let us do this:
user = Factory(:musical_user)
Instead of:
user = Factory(:email_confirmed_user)
Factory(:music_interest, :interested => user)
More intention-revealing. More encapsulation, protecting us from change.
Working with fakes
If you haven’t read it, read “Have you ever… faked it?”. We’re finding this to be a good approach for testing objects that interface with web services such as geocoding and payment processing.
class User < ActiveRecord::Base
acts_as_mappable
before_validation :geocode_location, :if => :location_changed?
def geocode_location
geo = Geokit::Geocoders::MultiGeocoder.geocode(location)
self.lat, self.lng = geo.lat, geo.lng
end
end
Factory.define :boston_user, :parent => :email_confirmed_user do |user|
user.location { "Boston, MA" }
user.after_build { |u|
Geokit::Geocoders::FakeGeocoder.locations["Boston, MA"] = [0, 1]
}
end
Enjoy!
Get version 1.2.3 with callbacks on Gemcutter:
sudo gem install factory_girl -s http://gemcutter.org
Happy testing.