Dependent factories in Factory Girl
I have 2 factories. Beta_user and Beta_invite. Basically before a Beta_user can validly save I have to create an entry of Beta_invite. Unfortunately these models don't have clean associations, but they do share an email field.
Factory.sequence :email do |n|
"email#{n}@factory.com"
end
#BetaInvite
Factory.define :beta_invite do |f|
f.email {Factory.next(:email)}
f.approved false
f.source "web"
end
#User
Factory.define :user do |f|
f.email {Factory.next(:email)}
f.password "password"
end
#User => BetaUser
Factory.define :beta_user, :parent => :user do |f|
f.after_build do |user|
if BetaInvite.find_by_email(user.email).nil?
Factory(:beta_invite, :email => user.email)
end
end
end
So in the beta beta_user factory I am trying to use the after_build call back to create the beta_invite factory.
However it seems to be acting async or something. Possibly doing the find_by_email fetch?
If I try this:
Factory(:beta_user)
Factory(:beta_user)
Factory(:beta_user)
I get a failure stating that there is no record of a beta_invite with that users email.
If ins开发者_开发问答tead I try:
Factory.build(:beta_user).save
Factory.build(:beta_user).save
Factory.build(:beta_user).save
I get better results. As if calling the .build method and waiting to save allows time for the beta_invite factory to be created. Instead of calling Factory.create directly. The docs say that in the case of calling Factory.create both the after_build and after_create callbacks get called.
Any help is much appreciated.
UPDATE:
So the User model I am using does a before_validation
call to the method that checks if there is a beta invite. If I move this method call to before_save
instead. It works correctly. Is there something i'm over looking. When does factory_girl run the after_build
and after_create
callbacks in relation to active-record's before_validation
and before_save
?
To me it seems like it just should be able to work, but I have had problems with associations in Factory-girl as well. An approach I like to use in a case like this, if the relations are less evident, is to define a special method, inside your factory as follows:
def Factory.create_beta_user
beta_invite = Factory(:beta_invite)
beta_user = Factory(:user, :email => beta_invite.email)
beta_user
end
and to use that in your tests, just write
Factory.create_beta_user
Hope this helps.
Not sure if this would help you but this is the code I used:
# Create factories with Factory Girl
FactoryGirl.define do
# Create a sequence of unique factory users
sequence(:email) { |n| "factoryusername+#{n}@example.com"}
factory :user do
email
password "factorypassword"
# Add factory user email to beta invite
after(:build) {|user| BetaInvite.create({:email => "#{user.email}"})}
end
end
I found this comment gave a really good example:
term = create(:term)
period = create(:period, term: term)
candidate = create(:candidate, term: term)
I applied it to my situation and can confirm it works.
精彩评论