Dependent factories in Factory Girl
Asked Answered
L

3

12

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?

Lannielanning answered 17/5, 2011 at 1:6 Comment(1)
Can you post your User class? The bug may be in the validations or how they're set up. Also it's odd b/c you said they don't have clean associations. What exactly do you mean by that? Showing your model code would help.Putman
P
12

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.

Pice answered 19/5, 2011 at 20:34 Comment(0)
T
7

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
Transfigure answered 17/9, 2012 at 1:58 Comment(1)
This solution is much more idiomatic to FactoryGirlCoraliecoraline
J
0

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.

Jaenicke answered 8/2, 2021 at 11:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.