Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 instead 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?

like image 543
kevzettler Avatar asked May 17 '11 01:05

kevzettler


2 Answers

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.

like image 129
nathanvda Avatar answered Oct 23 '22 16:10

nathanvda


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
like image 7
Carl Sednaoui Avatar answered Oct 23 '22 15:10

Carl Sednaoui