Factory Girl now has callbacks thanks to Nate Sutton.

There are three callbacks:
after_build
after_create
after_stub
When build(:user) is invoked, the after_build callback runs after building the user. Likewise for create and stub.
These come in handy in a number of common use cases.
Models:
class Article < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :article
end
Factories:
factory :article do
body 'password'
factory :article_with_comment do
after_create do |article|
create(:comment, article: article)
end
end
end
factory :comment do
body 'Great article!'
end
Nice. Callbacks let us do this:
article = create(:article_with_comment)
Instead of this:
article = create(:article)
create(:comment, article: article)
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 :user
email
password 'password'
factory :email_confirmed_user do
email_confirmed { true }
end
end
factory :topic do
name 'topic_name'
end
factory :interest
topic
interested factory: :user
end
factory :music_interest, class: 'Interest' do
topic.association(:topic, name: 'Music')
end
factory :sports_interest, class: 'Interest' do
topic.association(:topic, name: 'Sports')
end
And now the payoff:
factory :musical_user, parent: :email_confirmed_user do
after_create { |user| create(:music_interest, interested: user)
end
factory :sporty_user, parent: :email_confirmed_user do
after_create { |user| create(:sports_interest, interested: user) }
end
Again, factories let us do this:
user = create(:musical_user)
Instead of:
user = create(:email_confirmed_user)
create(:music_interest, interested: user)
More intention-revealing. More encapsulation, protecting us from change.
Fakes are 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 :user do
factory :boston_user do
location 'Boston, MA'
after_build do |user|
Geokit::Geocoders::FakeGeocoder.locations['Boston, MA'] = [0, 1]
end
end
end
Install:
gem 'factory_girl'
Happy testing.
Written by Dan Croak.