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?)
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!
.
ignore
has been deprecated and will be removed on version 5 of Factory Girl.
Instead, you can use transient attributes:
factory :dumas, :class => Customer do
transient do
female { Gender.find_by_code('Female') || create(:female) }
end
first_name 'Mary'
last_name 'Dumas'
gender { female }
end
The ignore
block can be used to wrap this up as well:
factory :dumas, :class => Customer do
ignore do
female { Gender.find_by_code('Female') || create(:female) }
end
first_name 'Mary'
last_name 'Dumas'
gender { female }
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With