FactoryGirl association model trouble: “SystemStac

2019-01-24 10:12发布

问题:

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