FactoryGirl association model trouble: “SystemStackError: stack level too deep”

柔情痞子 提交于 2019-11-27 16:19:37

问题


I am using Ruby on Rails 3.0.9, RSpec-rails 2 and FactoryGirl. I am trying to state a Factory association model but I am in trouble.

I have a factories/user.rb file like the following:

FactoryGirl.define do
  factory :user, :class => User do
    attribute_1
    attribute_2
    ...

    association :account, :factory => :users_account, :method => :build, :email => 'foo@bar.com'
  end
end

and a factories/users/account.rb file like the following:

FactoryGirl.define do
  factory :users_account, :class => Users::Account do
    sequence(:email) {|n| "foo#{n}@bar.com" }
    ...
  end
end

The above example works as expected in my spec files, but if in the factory :users_account statement I add the association :user code so to have

FactoryGirl.define do
  factory :users_account, :class => Users::Account do
    sequence(:email) {|n| "foo#{n}@bar.com" }
    ...
    association      :user
  end
end

I get the following error:

Failure/Error: Unable to find matching line from backtrace
SystemStackError:
  stack level too deep

How can I solve that problem so to access associated models from both sides\factories (that is, in my spec files I would like to use RoR association model methods like user.account and account.user)?

P.S.: I read the Factory Girl and has_one question and my case is very close to the case explained in the linked question. That is, I have an has_one association too (between User and Users::Account classes).


回答1:


According to the docs, you can't just put both sides of the associations into the factories. You'll need to use their after callback to set an object(s) to return.

For instance, in the factories/users/account.rb file, you put something like

after(:build) do |user_account, evaluator|
    user_account.user = FactoryGirl.build(:user, :account=>user_account)
end

For has_many associations, you'll need to use their *_list functions.

after(:build) do |user_account, evaluator|
    user_account.users = FactoryGirl.build_list(:user, 5, :account=>user_account)
end

Note: I believe the example in the docs is a bit misleading it doesn't assign anything to the object. I believe it should be something like (note the assignment).

# the after(:create) yields two values; the user instance itself and the
# evaluator, which stores all values from the factory, including ignored
# attributes; `create_list`'s second argument is the number of records
# to create and we make sure the user is associated properly to the post
after(:create) do |user, evaluator|
  user.posts = FactoryGirl.create_list(:post, evaluator.posts_count, user: user)
end



回答2:


Spyle's excellent answer (still working with Rails 5.2 and RSpec 3.8) will work for most associations. I had a use case where a factory needed to use 2 different factories (or different traits) for a single has_many association (ie. for a scope type method).

What I ended up coming up with was:

# To build user with posts of category == 'Special' and category == 'Regular'
after(:create) do |user, evaluator|
  array = []
  array.push(FactoryBot.create_list(:post, 1, category: 'Regular')
  array.push(FactoryBot.create_list(:post, 1, category: 'Special')
  user.posts = array.flatten
end

This allowed the user to have 1 post of category 'Regular' and 1 post of category 'Special.'



来源:https://stackoverflow.com/questions/7479031/factorygirl-association-model-trouble-systemstackerror-stack-level-too-deep

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!