Ruby-on-rails – Factory Girl create association with existing object

associationsfactory-botruby-on-rails

I am new to FactoryGirl and I am trying the following simple scenario?

factory :female, :class => Gender do
  code 'Female'
end

factory :male, :class => Gender do
  code 'Male'
end

factory :papas, :class => Customer do
  first_name 'Jim'
  last_name 'Papas'
  association :gender, :factory => :male, :strategy => :build
end

factory :dumas, :class => Customer do
  first_name 'Mary'
  last_name 'Dumas'
  association :gender, :factory => :female, :strategy => :build
end

Then in my test:

 create(:male)
 create(:female)
 create(:papas)
 create(:dumas)

Note that Customer class has an assocation belongs_to Gender class and a validation rule that says that gender_id should be present. I also have a validation on Gender class for uniqueness on code.

On create(:papas) statement above, in my test, I get the error that the Customer that is going to be created is not valid, because gender_id is nil.

If I remove the :strategy => :build on Customer :papas factory association with gender, then I will get an error, that when trying to create :papas, the code for the gender already exists.

What do I need to do so my tests create the data as required above?

Note that I want to have genders created without customers also, in other tests. Do not tell me to create the customers with factory create commands and let the customers create the genders at the same time. This will not work if I try to create two customers of the same gender too.

Also, there has to be a better answer than the one:

@male = create(:male)
@female = create(:female)
create(:papas, :gender => @male)
create(:dumas, :gender => @female)

(When using fixtures these things were ready out-of-the-box. Shall I return back to fixtures?)

Best Answer

You could achieve what you want by both not using a factory for your Gender instances and using callbacks in your Customer factory.

You're not gaining a lot from the Gender factory as it stands (although I appreciate it may be simplified for the purposes of asking this question). You could just as easily do this to get a Gender:

Gender.find_or_create_by_code!('Male')

Then in your Customer factory you can use a before callback to set the Gender:

factory :dumas, :class => Customer do
  first_name 'Mary'
  last_name 'Dumas'
  before_create do |customer|
    customer.gender = Gender.find_or_create_by_code!('Female')
  end
end

The format for using callbacks in factory_girl has recently changed so if you're using 3.3 or later you'll need to do:

before(:create) do |customer|

instead of before_create.

If creating a gender is more complex than your example you can still create the genders using a factory but hook them up with your customers using callbacks using find_by_code! insteand of find_or_create_by_code!.

Related Topic