FactoryGirl and has_many associations
If you’re writing acceptance or integration tests for your Rails app using tools like Cucumber or RSpec and Capybara, then you’ll likely be familiar with generating seed data for your tests using factories. FactoryGirl is one such factory tool, and it’s currently-in-beta syntax for defining factories is rather nice:
FactoryGirl.define do factory :venue do name 'The Batcave' address '5 Smith Street' end factory :gig do name 'The Who' venue end end
Here we have a factory for a Gig model that belongs to a Venue, and now the corresponding models:
class Venue < ActiveRecord::Base has_many :gigs validates_presence_of :name, :address end class Gig < ActiveRecord::Base belongs_to :venue validates_presence_of :name validates_presence_of :venue end
Now we can create a gig in our tests:
Since the factory for the gig includes the
venue association, this is also created, which ensures that the gig object is valid (since it requres the venue to be present).
This approach works nicely for models with a
belongs_to association, but what about
has_many? FactoryGirl provides callbacks that you can use to build the members of a
FactoryGirl.define do factory :venue_with_gigs, :parent => :venue do after_create do |venue| FactoryGirl.create(:gig, :venue => venue) end end end
Since having child gigs isn’t a requirement for venues, we’ve created another factory that extends the basic venue factory with and creates a child gig in the
after_create callback. But what happens if our venues actually require at least one child gig?
class Venue < ActiveRecord::Base has_many :gigs validates_presence_of :gigs end
after_create callbacks in the factory won’t work here because the initial creation of the venue will fail due to the empty set of gigs. Instead, we need to move the creation of the gigs into the basic venue factory, and use a different technique, this time in an
FactoryGirl.define do factory :venue do name 'The Batcave' address '5 Smith Street' after_build do |venue| venue.gigs << FactoryGirl.build(:gig, :venue => venue) end end end
after_build callback is useful here because it is called both when you run
FactoryGirl.build and also
FactoryGirl.create. When you do the latter, the new gig records are assigned to the gigs association via
FactoryGirl.build(:gig, :venue => venue). For example:
>> venue = FactoryGirl.create :venue => #<Venue id: 1, name: "The Batcave", created_at: "2011-03-23 06:01:37", updated_at: "2011-03-23 06:01:37", address: "5 Smith Street"> >> venue.gigs => [#<Gig id: 1, name: "The Who": venue_id: 1, created_at: "2011-03-23 06:02:18", updated_at: "2011-03-23 06:02:18">]
Now you are equipped to build factories that satisfy all kinds of ActiveRecord associations, and you can get back to testing!
 You might think that the
:venue => venue association is unnecessary, since the association with the venue is implied when the gig is pushed onto the venue’s
gigs object. However, it is necessary to do this within the context of these factories, since a gig built without an explicitly set
:venue will create a new venue, which in turn creates a new child gig, which in turn creates another venue, and before long you’re in “stack level too deep” country. That’s dangerous country.