I went back to using Fixtures. IMOP, fixtures are FAR better than factories; easier to use, easier to write, easier to understand (no magic). My suggestion: limit your testing library to the very basics (listen to DHH)...use minitest with fixtures.
In my app a district has many schools, a school has many uses, a user has many accounts, an account has one role. In order to create complete factories for testing I need to create a user and school that persists across factories. Im getting a "stack level too deep" 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 "[email protected]"
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 am attempting to do FactoryGirl.create(:account)...
which I am expecting to create an account, with the user and role from the factories above, with the user associated with the school that is associated with the district. This is not working for me. Among failing tests I get a "stack level too deep" error. And, I believe my before each DatabaseCleaner.clean is clearing the test db before each new factory.
The test that calls these factories is:
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 nest the factories correctly and get a current_user in order to continue testing?
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
Your basic problem is that you have a circular dependency between your user
factory and your school
factory, caused by the fact that you create a primarycontact
(a user) when you create a school, then that user creates a school, and so on.
You can get around this by changing how you define your school
association inside the user
factory. Before doing that though, I'd suggest as a general rule using the shorthand notation for associations. So 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 factories will do what you want without generating any circular 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 "[email protected]"
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
Notice that what I have done is to create a factory for primarycontact
with the class: "User"
option. Unlike the user
factory, this factory does not create the school
by default, avoiding the circular dependency.
Then in the school
factory, I use an after_build
callback to assign the school itself to the school
association on primarycontact
, rather than creating a new school (which was causing the problem in your factories).
Hope that makes sense. Note that the callback syntax has changed in the more recent version of factory_girl, see the documentation for details.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With