I have a class named MenuHeader with the following:
class MenuHeader < ActiveRecord::Base
acts_as_tree :parent_id
belongs_to :menu
I'd like to create several levels of menu_headers but am not sure how to do this:
FactoryGirl.define do
factory :menu_header do
name "my menu header"
menu
after(:create) { |menu_header| do_something_else_to(user) }
end
end
Would I create a :menu_header2 or can I some how just extend the above. How do I set the :parent_id to the value of the recently created menu_header?
thx
I'd create different level of menu_header factories and build their children using after(:build). btw I prefer after :build because I can easily create non-persisted objects for testing.
FactoryGirl.define do
factory :menu_header do
name "my menu header"
menu
factory :menu_header_level1 do
after(:build) do |m|
m.children << FactoryGirl.build(:menu_header, :name => 'level0 submenu0', :parent => m)
m.children << FactoryGirl.build(:menu_header, :name => 'level0 submenu1', :parent => m)
end
end
factory :menu_header_level2 do
after(:build) do |m|
m.children << FactoryGirl.build(:menu_header_level1, :name => 'level1 submenu0', :parent => m)
end
end
end
end
Related
Here's my models :
class Parent < ActiveRecord::Base
has_many :children
accepts_nested_attributes_for :children
validate :parent_must_have_child_status_1
def parent_must_have_child_status_1
errors.add(:base, :no_child_status_1) if children.all? {|c| c.status != 1}
end
end
class Child < ActiveRecord::Base
belongs_to :parent
accepts_nested_attributes_for :parent
validates :parent, :presence => true
end
and my factories :
factory :parent do
children { [FactoryGirl.build(:child, :status=>1)] }
end
factory :child do
parent
end
and result of FactoryGirl.create
FactoryGirl.create(:parent) #=> SystemStackError: stack level too deep
FactoryGirl.create(:child) #=> SystemStackError: stack level too deep
I want to solve those errors.
I tried several things but I couldn't solve them.
How could I create a parent and child factory in this situation?
Do you have an idea?
Thanks!
the problem is in your factories:
I above code, i you create a parent it will create a child which again create a parent object .this will recursively happen thats y u r getting "Stack ?Level Too Deep" error.
Same for the child also.
Maybe you should try this
factory :parent do |p|
p.sequence(:name) {|n| "John #{n}" }
end
factory :child do |c|
c.parents {|parents| [parents.association(:parent)] }
end
I use to do something like this:
factory :parent do
after(:create) do |parent|
parent.children << FactoryGirl.create(:child, :status=>1)
end
end
It works pretty well.
I have the following AR has_many, belongs_to relationships:
League --> Conference --> Division --> Team
I have an Event model that looks like this:
class Event < ActiveRecord::Base
belongs_to :league
belongs_to :home_team, :class_name => 'Team', :foreign_key => :home_team_id
belongs_to :away_team, :class_name => 'Team', :foreign_key => :away_team_id
validate :same_league
def same_league
return if home_team.blank? || away_team.blank?
errors.add :base, "teams must be in the same league" if home_team.league != away_team.league
end
end
And some factories:
FactoryGirl.define do
factory :league do
name 'NFL'
end
end
Factory.define :conference do |f|
f.name 'NFC'
f.association :league
end
Factory.define :division do |f|
f.name 'North'
f.association :conference
end
Factory.define :team do |f|
f.name 'Packers'
f.locale 'Green Bay'
f.association :division
end
FactoryGirl.define do
factory :event do
association :league
association :home_team, :factory => :team
association :away_team, :factory => :team
end
end
So with all that, how would I go about writing a spec for the same_league validation method?
describe Event do
pending 'should not allow home_team and away_team to be from two different leagues'
end
My issue is knowing what the simplest way to go about creating two teams in different leagues and associating one with home_team and the other with away_team in the event model.
You can store instances you generate with factories and then explicitly use their ID's to fill in the foreign keys for subsequent factories.
Here I'm creating two leagues, then setting up two tests. One where the event has two teams in the same league and another with two teams in different leagues. This way I can test if the event object is properly validating:
describe Event do
before(:each) do
#first_league = Factory(:league)
#second_league = Factory(:league)
end
it "should allow the home_team and away_team to be from the same league" do
home_team = Factory(:team, :league_id => #first_league.id)
away_team = Factory(:team, :league_id => #first_league.id)
event = Factory(:event, :home_team_id => home_team.id, :away_team_id => away_team.id)
event.valid?.should == true
end
it "should not allow the home_team and away_team to be from two different leagues" do
home_team = Factory(:team, :league_id => #first_league.id)
away_team = Factory(:team, :league_id => #second_league.id)
event = Factory(:event, :home_team_id => home_team.id, :away_team_id => away_team.id)
event.valid?.should == false
end
end
I'm using factory_girl_rails instead of fixtures. Here are my models:
class User < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :user
end
Here are my factories:
Factory.define :user do |u|
end
Factory.define :task do |t|
t.association :user, :factory => :user
end
In a test I do this:
user = Factory.create :user
(1..5).each { Factory.create(:task, :user => user)}
The problem that I'm experiencing is that afterward user.tasks contains only one task.
I have tried defining the user factory like this:
Factory.define :user do |u|
u.tasks {|tasks| [tasks.association(:user)] }
end
and like this:
Factory.define :user do |u|
u.tasks {|tasks| [tasks.association(:user), tasks.association(:user)] }
end
In both cases Factory.create(:user) causes an infinite loop.
I think what you're doing is backwards -- you can create Tasks from the User, but not visa versa like you're trying to do. Conceptually, the association goes from the user to the task.
What I did was create a special factory for that type of user.
Factory.define :user_with_tasks, :parent => :user do |user|
user.after_create {|u| 5.times { Factory.create(:task, :user => user) } }
end
Also, if you just want to use the default factory, you just do this
Factory.define :task do |f|
f.user :user
end
No need to specify the factory
UPDATE: Looks like others have done something similar in the past:
Populating an association with children in factory_girl
Ah, this works:
Factory.define :task do |t|
t.association :user
t.after_create {|t| t.user.tasks << t}
end
John, your approach would work but I actually couldn't use what you described because in my test the tasks need to also reference another (unmentioned) model and I don't see a way of referencing it in the user_with_tasks factory definition.
I need to pass extra arguments to factory girl to be used in a callback. Something like this (but more complex really):
Factory.define :blog do |blog|
blog.name "Blah"
blog.after_create do |blog|
blog.posts += sample_posts
blog.save!
end
end
and then create it with something like this:
Factory.create(:blog, :sample_posts => [post1, post2])
Any ideas how to do it?
This is now possible without any "hacks" thanks to transient attributes (see comment on issue #49)
example:
FactoryGirl.define do
factory :user do
transient do
bar_extension false
end
name {"foo #{' bar' if bar_extension}"}
end
end
# Factory(:user).name = "foo"
# Factory(:user, :bar_extension => true).name = "foo bar"
For Factory Girl versions < 5.0:
FactoryGirl.define do
factory :user do
ignore do
bar_extension false
end
name {"foo #{' bar' if bar_extension}"}
end
end
# Factory(:user).name = "foo"
# Factory(:user, :bar_extension => true).name = "foo bar"
One option would be to create a virtual accessor for extra posts that the after_create hook checks:
class Blog
has_many :posts
attr_accessible :name, :title, ... # DB columns
attr_accessor :sample_posts # virtual column
end
Factory.define :blog do |blog|
blog.name 'Blah'
blog.after_create do |b|
b.posts += b.sample_posts
b.save!
end
end
Factory(:blog, :sample_posts => [post1, post2])
Apparently, this is not possible at the moment without workarounds that require modifying the model itself. This bug is reported in: http://github.com/thoughtbot/factory_girl/issues#issue/49
Another option would be to use build instead of create and add :autosave to the collection:
class Blog
has_many :posts, :autosave => true
end
Factory.define :blog do |blog|
blog.name 'Blah'
blog.posts { |_| [Factory.build(:post)] }
end
Factory(:blog, :posts => [post1, post2])
#or
Factory.build(:blog, :posts => [unsavedPost1, unsavedPost2])
if you are opening the class inside the factorygirl file, i suggest doing it like
require "user"
class User
attr :post_count
end
so that you are opening the class, instead of overwriting it
Given the following
class User < ActiveRecord::Base
has_and_belongs_to_many :companies
end
class Company < ActiveRecord::Base
has_and_belongs_to_many :users
end
how do you define factories for companies and users including the bidirectional association? Here's my attempt
Factory.define :company do |f|
f.users{ |users| [users.association :company]}
end
Factory.define :user do |f|
f.companies{ |companies| [companies.association :user]}
end
now I try
Factory :user
Perhaps unsurprisingly this results in an infinite loop as the factories recursively use each other to define themselves.
More surprisingly I haven't found a mention of how to do this anywhere, is there a pattern for defining the necessary factories or I am doing something fundamentally wrong?
Here is the solution that works for me.
FactoryGirl.define do
factory :company do
#company attributes
end
factory :user do
companies {[FactoryGirl.create(:company)]}
#user attributes
end
end
if you will need specific company you can use factory this way
company = FactoryGirl.create(:company, #{company attributes})
user = FactoryGirl.create(:user, :companies => [company])
Hope this will be helpful for somebody.
Factorygirl has since been updated and now includes callbacks to solve this problem. Take a look at http://robots.thoughtbot.com/post/254496652/aint-no-calla-back-girl for more info.
In my opinion, Just create two different factories like:
Factory.define :user, :class => User do |u|
# Just normal attributes initialization
end
Factory.define :company, :class => Company do |u|
# Just normal attributes initialization
end
When you write the test-cases for user then just write like this
Factory(:user, :companies => [Factory(:company)])
Hope it will work.
I couldn´t find an example for the above mentioned case on the provided website. (Only 1:N and polymorphic assocations, but no habtm). I had a similar case and my code looks like this:
Factory.define :user do |user|
user.name "Foo Bar"
user.after_create { |u| Factory(:company, :users => [u]) }
end
Factory.define :company do |c|
c.name "Acme"
end
What worked for me was setting the association when using the factory.
Using your example:
user = Factory(:user)
company = Factory(:company)
company.users << user
company.save!
Found this way nice and verbose:
FactoryGirl.define do
factory :foo do
name "Foo"
end
factory :bar do
name "Bar"
foos { |a| [a.association(:foo)] }
end
end
factory :company_with_users, parent: :company do
ignore do
users_count 20
end
after_create do |company, evaluator|
FactoryGirl.create_list(:user, evaluator.users_count, users: [user])
end
end
Warning: Change users: [user] to :users => [user] for ruby 1.8.x
For HABTM I used traits and callbacks.
Say you have the following models:
class Catalog < ApplicationRecord
has_and_belongs_to_many :courses
…
end
class Course < ApplicationRecord
…
end
You can define the Factory above:
FactoryBot.define do
factory :catalog do
description "Catalog description"
…
trait :with_courses do
after :create do |catalog|
courses = FactoryBot.create_list :course, 2
catalog.courses << courses
catalog.save
end
end
end
end
First of all I strongly encourage you to use has_many :through instead of habtm (more about this here), so you'll end up with something like:
Employment belongs_to :users
Employment belongs_to :companies
User has_many :employments
User has_many :companies, :through => :employments
Company has_many :employments
Company has_many :users, :through => :employments
After this you'll have has_many association on both sides and can assign to them in factory_girl in the way you did it.
Update for Rails 5:
Instead of using has_and_belongs_to_many association, you should consider: has_many :through association.
The user factory for this association looks like this:
FactoryBot.define do
factory :user do
# user attributes
factory :user_with_companies do
transient do
companies_count 10 # default number
end
after(:create) do |user, evaluator|
create_list(:companies, evaluator.companies_count, user: user)
end
end
end
end
You can create the company factory in a similar way.
Once both factories are set, you can create user_with_companies factory with companies_count option. Here you can specify how many companies the user belongs to: create(:user_with_companies, companies_count: 15)
You can find detailed explanation about factory girl associations here.
You can define new factory and use after(:create) callback to create a list of associations. Let's see how to do it in this example:
FactoryBot.define do
# user factory without associated companies
factory :user do
# user attributes
factory :user_with_companies do
transient do
companies_count 10
end
after(:create) do |user, evaluator|
create_list(:companies, evaluator.companies_count, user: user)
end
end
end
end
Attribute companies_count is a transient and available in attributes of the factory and in the callback via the evaluator. Now, you can create a user with companies with the option to specify how many companies you want:
create(:user_with_companies).companies.length # 10
create(:user_with_companies, companies_count: 15).companies.length # 15