Factory Girl and Creating a Record Depending on Children

Suppose you are creating a record which holds a Sauce and percentages or ingredients that must add up to 100%. You have the parent record, the Sauce and the children Sauce Components.

Tips on Using Factory Girl to Create a Record with Mandatory Children

This article has the right format which is not what you’d expect

FactoryGirl.define do
  factory :recipe do
    name 'Secret Sauce'
    after_build do |sauce|
      sauce << FactoryGirl.build(:sauce_component, percentage: 0.60, sauce: sauce)
      sauce << FactoryGirl.build(:sauce_component, percentage: 0.40, sauce: sauce)


  1. The use of the <<
  2. The inclusion of the sauce for the FactoryGirl.build even though we’re using <<
  3. The use of after_build

Validate presence of the belongs_to foreign key with the object, not the id

Use the association not the association_id when doing validations for presence in Rails! Otherwise, the validator complains the id doesn’t exist, and you can’t get FactoryGirl to save the parent record.

Thus, the right validation in the SauceComponent model is

validates :sauce, presence: true

This one prevents the FactoryGirl syntax from running, because the sauce_id is nil at first, so the records don’t save. Don’t do this one!

validates :sauce_id, presence: true

And it’s SUPER important to specify the has_many and belongs_to using the inverse_of parameter.

Otherwise, you’ll have issues with validating for the presence of the Sauce in the SauceComponent because they get created at the same time.

Accepts Nested Attributes

This article is helpful:

When displaying 2 levels of nesting, it’s incredibly important to check for sauce_component.object.marked_for_destruction? when looping through the child records:

= f.simple_fields_for :sauce_components do |sauce_component|
  - unless sauce_component.object.marked_for_destruction?
     = render 'sauce_component_fields', :f => sauce_component

Otherwise, if you get a validation error, you’ll display the record that you deleted, which is very confusing!

1 Like