factory_bot: Problems with factory_girl and has_many / belongs_to associations

Good morning everyone, Yesterday I started to use factory_girl with Rails. My setup features two classes with has_many / belongs_to relation between them. I literally followed the instructions for the has_many case here. My current code features this:

class User < ActiveRecord::Base
  has_many :assistants, dependent: :destroy
end

class Assistant < ActiveRecord::Base
  belongs_to :user
end

FactoryGirl.define do
  factory :assistant do
    name { Faker::Name.name }
    user
  end

  factory :user do
    name { Faker::Name.name }
    email { Faker::Internet.email }

    factory :user_with_assistants do
      ignore do
        assistants_count 5
      end

      after(:create) do |user, evaluator|
        FactoryGirl.create_list(:assistant, evaluator.assistants_count, user: user)
      end
    end
  end
end

My current problem is that the same kind of expression you use in the example does not work for me in the same way:

# I get this:
FactoryGirl.create(:user_with_assistants).assistants.length # => 0

I tried to dig a bit further and discovered the following:

# In Rails console
user = FactoryGirl.create(:user_with_assistants)
user.assistants.length # => 0
Assistant.where(user_id: user.id).all.length # => 5

As you can see, the list of assistants gets created correctly, but it is not available through the ActiveRecord association, and I need it to be available through the association to be able to test the relation between my two classes.

Obviously, I checked if the association works at all, and it does:

user = User.create(name: "Jony Ive", email: "jive@apple.com")
user.assistants.create(name: "Design Intern")
user.assistants.length # => 1

I will be very very thankful for any hints on what I am doing wrong.

About this issue

  • Original URL
  • State: closed
  • Created 11 years ago
  • Reactions: 2
  • Comments: 38 (3 by maintainers)

Commits related to this issue

Most upvoted comments

I’m hesitant to add noise here, but I just wasted ~1 hour because of this issue. Perhaps, if nothing is done on the library side, the associations documentation could have a note added that mentions this behavior.

@appplemac if you put a user.reload in the after(:create) callback:

after(:create) do |user, evaluator|
  FactoryGirl.create_list(:assistant, evaluator.assistants_count, user: user)
  user.reload
end

Does that fix things?

@appplemac what happens is when the assistants get created, they associate themselves to the user but user doesn’t know about the changes to its internal state. Calling user.reload refreshes itself, pulling in newly created data that it didn’t know about before (in this case, the assistants).

+1, this is really annoying

What about traits? Not sure on the internals of this vs the factory method, or how it differs, but we use this quite a bit:

FactoryGirl.define do
  factory :assistant do
    name { Faker::Name.name }
    user
  end

  factory :user do
    name { Faker::Name.name }
    email { Faker::Internet.email }

    trait :with_assistants do
      ignore do
        assistants_count 5
      end

      after(:create) do |user, evaluator|
        FactoryGirl.create_list(:assistant, evaluator.assistants_count, user: user)
      end
    end
  end
end

user = FactoryGirl.create(:user, :with_assistants, assistants_count: 3)
user.assistants.count

Also have done traits this way:

trait :with_assistants do
  transient do
    assistants_count 1
  end

  after(:create) do |user, evaluator|
    evaluator.assistants_count.times do
      user.assistants << create(:assistant)
    end
  end
end

What is the status of this issue? Was it fixed? According to the documentation, using create_list in after(:create) should properly update both ends of the has_many relation. However, using factory_girl (4.5.0) I can still experience the issue.

If current object runs save validations that depend on the relationship with its has_many objects, the workaround of calling reload in after(:create) won’t work.