Populating an association with children in factory_girl
Asked Answered
E

5

42

I have a model Foo that has_many 'Bar'. I have a factory_girl factory for each of these objects. The factory for Bar has an association to Foo; it will instantiate a Foo when it creates the Bar.

I'd like a Factory that creates a Foo that contains a Bar. Ideally this Bar would be created through the :bar factory, and respect the build strategy (create/build) used to create the Foo.

I know I could just call the :bar factory and then grab the Foo reference from the new Bar. I'd like to avoid this; in my test case, the important object is Foo; calling the Bar factory seems a bit circuitous. Also, I can see the need for a Foo with multiple Bars.

Is this possible in factory_girl? How do you define this relationship in the parent?

Elsi answered 30/5, 2010 at 2:34 Comment(0)
E
51

The Factory.after_ hooks appear to be the only way to do this successfully. I've figured out a way to maintain the build strategy without duplicating code:

Factory.define :foo do |f|
  f.name "A Foo"
  f.after(:build) { |foo|
    foo.bars << Factory.build(:bar, :foo => foo)
  }
  f.after(:create) { |foo|
    foo.bars.each { |bar| bar.save! }
  }
end

The documentation states that after_build will be called before after_create if the :create build strategy is used. If :build is used, then only after_build is called, and everyone is happy.

I've also created an abstracted generally-applicable version at this gist to keep things DRY.

Elsi answered 13/6, 2010 at 18:45 Comment(3)
It is hard to believe that this is the best way to do this but It still seems to be the case. Up-voted this answer.Voracity
How do you use your gist version? What's the best way to incorporate that into a Rails app?Inquiline
As noted here, using << to add an association persists the changes. To avoid that, the after(:build) should be foo.association(:bars).add_to_target(Factory.build(:bar, :foo => foo) )Plexor
R
4

You can use the association method both ways:

Factory.define :foo do |f|
  # ...
  f.association :bar
end

If that won't work, you can associate them manually using a callback. Here's an example from one of my apps:

Factory.define :live_raid do |raid|
end

Factory.define :live_raid_with_attendee, :parent => :live_raid do |raid|
  raid.after_create { |r| Factory(:live_attendee, :live_raid => r) }
end
Rid answered 30/5, 2010 at 19:52 Comment(1)
The first syntax causes a a stack overflow/infinite recursion in my code. The second syntax works wonderfully, except that it doesn't obey the build strategy.Elsi
H
3

FactoryGirl now has a :method => :build option you can use on the association, which will build the associated object rather than creating it.

#64: Building an object creates associations

Humfrid answered 12/11, 2011 at 14:44 Comment(0)
K
3

FactoryGirl 4.3.0 is calling save! on an association when calling build on the parent object, which i believe is not intended to be the correct behavior.

After digging through the FactoryGirl code, adding strategy: :build to the association definition in the factory seems now be creating my association without calling save!.

Kraft answered 29/12, 2013 at 8:16 Comment(0)
A
2

Using factory_girl-4.5.0, create n child objects in a parent object factory

FactoryGirl.define do
  factory :foo do
    name "test"        

    after(:build) do |instance|
      n.times { instance.bars << FactoryGirl.create(:bar) }          
    end
  end
end
Auklet answered 15/4, 2015 at 13:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.