Factory girl, dependent factories

UPDATE

I returned to using Fixtures. IMOP, FAR lights are better than factories; easier to use, easier to write, easier to understand (no magic). My suggestion is: limit your test library to the base itself (listen to DHH) ... use minitest with devices.

original message

In my application in the district there are many schools, the school has many uses, the user has many accounts, the account has one role. To create complete factories for testing, I need to create a user and a school, which is stored in all factories. I get a "too high stack level" error in my recent attempts.

My user_test.rb

FactoryGirl.define do factory :district do name "Seattle" end factory :school do association :primarycontact, factory: :user # expecting this to attach the user_id from factory :user as :primary contact_id in the school model association :district, factory: :district # expecting this to attach the :district_id from the :district factory as :district_id in the school model name "Test School" end factory :user do, aliases: [:primarycontact] email " adam@example.com " name "Who What" username "wwhat" password "123456" password_confirmation { |u| u.password } association :school, factory: :school # expecting this to create :school_id in the users model, using the :school factory end factory :role do name "student" end factory :account do association :user, factory: :user association :role, factory: :role end end 

So, I'm trying to make FactoryGirl.create(:account)... which I expect to create an account with the user and the role from the factories above, with the user associated with the school that is associated with the district. This does not work for me. Among unsuccessful tests, I get the error "too high stack level". And I believe that before each DatabaseCleaner.clean clears the db test before each new factory.

The test that calls these plants:

 describe "User integration" do def log_em_in visit login_path fill_in('Username', :with => "wwhat") fill_in('Password', :with => "123456") click_button('Log In') end it "tests log in" do user = FactoryGirl.create(:account) log_em_in current_path.should == new_user_path end end 

.

 current_path.should == new_user_path returns unknown method error 'should' 

How can I improve this code to insert factories correctly and get current_user to continue testing?

MODELS

school.rb

  belongs_to :district belongs_to :primarycontact, :class_name => "User" has_many :users, :dependent => :destroy 

user.rb

  belongs_to :school has_many :accounts, :dependent => :destroy 

district.rb

  has_many :schools 

account.rb

  belongs_to :role belongs_to :user 

role.rb

  has_many :accounts has_many :users, :through => :accounts 
+4
source share
1 answer

Your main problem is that you have a circular relationship between your user factory and your school factory, caused by the fact that you create a primarycontact (user) when creating a school, then this user creates a school, etc.

You can get around this by changing how you define your school association inside the user factory. Before doing this, I would suggest using a shorthand notation for associations as a general rule. Therefore replace this:

 factory :account do association :user, factory: :user association :role, factory: :role end 

with this:

 factory :account do user role end 

Using this simplification, the following plants will do what you want without creating any cyclical dependency:

 FactoryGirl.define do factory :district do name "Seattle" end factory :school do |school| district primarycontact name "Test School" after_build do |s| s.primarycontact.school = s end end factory :user do email " adam@example.com " name "Who What" username "wwhat" password "123456" password_confirmation { |u| u.password } school end factory :primarycontact, class: "User" do # add any attributes you want the primarycontact user to have here end factory :role do name "student" end factory :account do user role end end 

Note what I did to create a factory for primarycontact with the class: "User" option. Unlike user factory, this factory does not create school by default, avoiding circular dependency.

Then in the school factory, I use the after_build to assign the school association school to primarycontact , and not to create a new school (which caused a problem in your factories).

Hope this makes sense. Note that the callback syntax has changed in a later version of factory_girl, see the documentation for details.

+11
source

All Articles