FactoryGirl has_many association with validation

2019-01-17 15:48发布

问题:

I have a standard has_many relationship (Booking has many Orders) with validation that a Booking does not get saved without at least one Order. I'm trying to replicate this with my FactoryGirl factories but the validation is preventing me from doing so.

class Booking < ActiveRecord::Base
  has_many :orders
  validates :orders, presence: true
end

class Order < ActiveRecord::Base
  belongs_to :booking
end

Here are my FactoyGirl factory specifications for each model as followed from FactoryGirl's GitHub wiki page.

FactoryGirl.define do                                                    

  factory :booking do                                                                                                                   
    factory :booking_with_orders do

      ignore do                                                                                                                         
        orders_count 1                                                                                                                  
      end                                                                                                                               

      before(:create) do |booking, evaluator|                                                                                           
        FactoryGirl.create_list(:order, evaluator.orders_count, booking: booking)                                                       
      end                                                                                                                               
    end                                                                                                                                 
  end 

  factory :order do
    booking
  end

end 

When I try to run FactoryGirl.create(:booking_with_orders) from my spec, I get:

Failure/Error: @booking = FactoryGirl.create(:booking_with_orders)
ActiveRecord::RecordInvalid:
  Validation failed: Orders can't be blank

It seems like the check for the validation is running even before before(:create) [...] which would theoretically create the Orders for the Booking.

This post recommends not adding has_many relationships to your factories but I would like to solve this anyways if there is a good way to do it.

Thanks in advance.

回答1:

Wat? Impossible? Not at all.

Just change your code to something like this:

after :build do |booking, evaluator|
  booking.orders << FactoryGirl.build_list(:order, evaluator.orders_count, booking: nil)
end


回答2:

Taking off from @jassa's answer, if you just need to add a single (required) associated record with a specific attribute, this pattern worked for me:

factory :booking do
  ignore do
    order_name "name"
  end

  after :build do |factory, evaluator|
    factory.orders << FactoryGirl.build(:order, name: evaluator.order_name, booking: nil)
  end
end


回答3:

This seems like an overly simplistic observation but what you're trying to do is in effect make sure that the Order exists before the Booking, which is impossible, as the Order cannot exist without its booking_id (meaning that the Booking needs to be created first).

There's nothing wrong with a has_many relationship in your factories, it's your validation that is a problem. Does this currently work in your application? How do you save your records in that case? What is the flow for creating Orders and Bookings?

Even the infamous accepts_nested_attributes_for won't help you here.

My advice is to rethink your record saving and validation strategy so that it is a little more sane.