In my application an account can have a single owner (user) and multiple users.
In my tests I do this:
# account_factory_static.rb
FactoryGirl.define do
factory :account do
name 'demoaccount'
association :owner, :factory => :user
end
end
# user_factory_static.rb
FactoryGirl.define do
factory :user do
email 'demo#example.com'
first_name 'Jon'
last_name 'Doe'
password 'password'
end
end
and use them like below:
let(:account) { FactoryGirl.create(:account) }
The problem is that right nowaccount.users.count equals 0 because I have no way to do something like #account.users << #account.owner like I do in my controllers when a user signs up.
The question is how can I add the associated account's id to the account_id attribute of the user in FactoryGirl?
In other words how do you do it in FactoryGirl?
Thanks.
You can use after :create block for it:
FactoryGirl.define do
factory :account do
name 'demoaccount'
association :owner, :factory => :user
after :create do |account|
account.users << account.owner
end
end
end
Related
I m new to factory bot, I try to create a sample data using factory bot but I got this error
How to resolve this error?
features/support/factories.rb:
require 'factory_bot'
FactoryBot.define do
factory :user do
email "xxx123#xyz.co"
password "asdf123"
password_confirmation "asdf123"
end
end
FactoryBot.define do
factory :post do
user = FactoryBot.create(:user)
end
end
require 'factory_bot'
FactoryBot.define do
factory :user do
email "xxx123#xyz.co"
password "asdf123"
password_confirmation "asdf123"
end
end
FactoryBot.define do
factory :post do
user
end
end
as described in FactoryBot documentation
Associations
It's possible to set up associations within factories. If the factory name is the same as the association name, the factory name can be left out.
factory :post do
# ...
author
end
You can also specify a different factory or override attributes:
factory :post do
# ...
association :author, factory: :user, last_name: "Writely"
end
I want create a factory for a Relationship model which contains two attributes followed_id and follower_id but i have no idea how to do this, this is my factories file :
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "Person #{n}" }
sequence(:email) { |n| "person_#{n}#example.com"}
password "foobar"
password_confirmation "foobar"
end
factory :relationship do
# i need something like this
# followed_id a_user.id
# follower_id another_user.id
end
end
update
what i want to do with this relationship factory is to test that if i destroy a user, all his relationships will be destroyed too, this is my test :
describe "relationships associations" do
let!(:relationship) { FactoryGirl.create(:relationship) }
it "should destroy associated relationships" do
relationships = #user.relationships.to_a
#user.destroy
expect(relationships).not_to be_empty
relationships.each do |relationship|
expect(Relationships.where(id: relationship.id)).to be_empty
end
end
end
In my experience such "relationship" factory is rarely needed in test. Instead, "user_with_followers" and "user_following_some_ones" are often used.
factory :user do
sequence(:name) { |n| "Person #{n}" }
sequence(:email) { |n| "person_#{n}#example.com"}
password "foobar"
password_confirmation "foobar"
factory :user_with_followers do
ignore do
followers_count 5
end
after_create do |user, evaluator|
followers = FactoryGirl.create_list(:user, evaluator.followers_count)
followers.each do |follower|
follower.follow(user) # Suppose you have a "follow()" method in User
end
end
factory :user_following_some_ones do
# Do the similar
end
end
# Use
FactoryGirl.create :user_with_followers
use association
factory :relationship do |r| # 'r' is how you call relationship in the block
...
r.association :followed #relationship is associated with followed user
#(i'm not sure how your application is set up,
#so you'll have to do this as best makes sense.
#is followed an attribute of user?
#then it would look like `r.association :user`
f.association :follower #same here
end
In the more recent versions of FactoryGirl, you should be able to do this:
factory :relationship do
association :followed, :factory => :user
association :follower, :factory => :user
end
What each of those two association lines does is set up a user instance (using your :user factory), and then assign to followed or follower of the current relationship instance.
Note that you need to specify the factory unless the association name and factory name are the same.
Update:
When creating the Relationship, specify :followed or :follower (whichever is applicable to you). Otherwise, it creates new user records for each of those and uses them.
FactoryGirl.create(:relationship, :followed => #user)
I have 2 models - User and Teacher. Teacher belongs_to User, User has Teacher.
So, i use Factory girl gem:
Factory.define :user do |user|
user.user_login "Another User"
user.user_role "admin"
user.password "foobar"
end
Factory.sequence :user_login do |n|
"person-#{n}"
end
Factory.define :teacher do |teacher|
...
teacher.user
end
I met problem and i don't understand how to solve that. When i create user via factory i can easily write:
#user = Factory( :user, :user_login => Factory.next(:user_login) )
And this creates user with inique login.
How can i do same thing for teacher? I tried that:
#teacher = Factory( :teacher, :user_login => Factory.next(:user_login) )
And it doesn't work.
You don't have to specify sequences separately and then pass them to another factory - you can use them inside factories like this:
Factory.define :user do |user|
# ...
user.sequence(:user_login) { |n| "person=#{n}" }
end
or shorter
Factory.define :user do
# ...
sequence(:user_login) { |n| "person=#{n}" }
end
Then, to association a user with teacher:
Factory.define :teacher do
association :user
end
Then you can just call
#teacher = Factory(:teacher)
which will automatically create the associated user with the next user_login in the sequence.
I solved that.
#teacher = Factory( :teacher,
:user => Factory(:user, :user_login => Factory.next(:user_login)) )
I have an Account model that has_one User model, and a User model that belongs_to Account model. I think that the basic code required for demonstration is:
class Account < ActiveRecord::Base
has_one :user
validates_presence_of :user
accepts_nested_attributes_for :user
end
class User < ActiveRecord::Base
belongs_to :account
# validates_presence_of :account # this is not actually present,
# but is implied by a not null requirement
# in the database, so it only takes effect on
# save or update, instead of on #valid?
end
When I define associations in each factory:
Factory.define :user do |f|
f.association :account
end
Factory.define :account do |f|
f.association :user
end
I get a stack overflow, as each is creating an account/user recursively.
The way I've been able to solve this is to emulate nested attribute forms in my tests:
before :each do
account_attributes = Factory.attributes_for :account
account_attributes[:user_attributes] = Factory.attributes_for :user
#account = Account.new(account_attributes)
end
However, I'd like to keep this logic in the factory, as it can get out of hand once I start adding other modules:
before :each do
account_attributes = Factory.attributes_for :account
account_attributes[:user_attributes] = Factory.attributes_for :user
account_attributes[:user_attributes][:profile_attributes] = Factory.attributes_for :profile
account_attributes[:payment_profile_attributes] = Factory.attributes_for :payment_profile
account_attributes[:subscription_attributes] = Factory.attributes_for :subscription
#account = Account.new(account_attributes)
end
Please help!
I was able to solve this problem by using factory_girl's after_build callback.
Factory.define :account do |f|
f.after_build do |account|
account.user ||= Factory.build(:user, :account => account)
account.payment_profile ||= Factory.build(:payment_profile, :account => account)
account.subscription ||= Factory.build(:subscription, :account => account)
end
end
Factory.define :user do |f|
f.after_build do |user|
user.account ||= Factory.build(:account, :user => user)
user.profile ||= Factory.build(:profile, :user => user)
end
end
This will create the associated classes before the owning class is saved, so validations pass.
Have a look at the factory_girl documentation. The way you're building those accounts seems like you're not really taking advantage of factory_girl.
I've always taken care of associations by creating the objects that I need before testing. I'm going to take a stab at this based on the models you're referencing above:
before :each do
#account = Factory(:account, :user_id => Factory(:user).id, :profile_id => Factory(:profile).id)
end
Now #account will have #account.user and #account.profile available. If you need to define those, #profile = #account.profile works just great.
I'm using Factory Girl/Rspec2/Rails 3.
In factories.rb, I have:
Factory.define :user do |user|
user.name 'Some guy'
user.email 'some_guy#somewhere.org'
user.password 'password'
end
Factory.define :org_admin, :parent => :user do |user|
user.email 'org_admin#somehwere.org'
end
Factory.define :user_with_membership_request, :parent => :user do |user|
user.email 'user_with_membership_request#somehwere.org'
end
Factory.define :organization do |org|
org.name 'MEC'
org.description 'Mountain Equipment Co-op'
end
Factory.define :membership do |membership|
membership.user { Factory(:user) }
membership.organization { Factory(:organization) }
end
Factory.define :admin_membership, :parent => :membership do |membership|
membership.user { Factory(:org_admin) }
membership.is_admin true
membership.status 'active'
end
Factory.define :membership_request, :parent => :membership do |membership|
membership.user { Factory(:user_with_membership_request) }
membership.status 'requested'
end
and then in my rspec test I have:
it 'should accept the membership request' do
#org_admin = Factory(:org_admin)
test_sign_in(#org_admin)
#organization = Factory(:organization)
#membership_request = Factory(:membership_request)
put :update, :organization_id => #organization.id, :id => #membership_request.id, :acceptance => 'approve'
...
end
When I run the test, I get:
Failure/Error: #membership_request = Factory(:membership_request)
Validation failed: Name has already been taken
I understand the reason for the failure is because FactoryGirl is creating another organization (with the same name).
But what I'd like to do is create several memberships all associated with the same organization. How do I do that?
Thanks.
Sean
You could check for an existing organization and use it, or create one if none exists:
Factory.define :membership do |membership|
membership.user { Factory(:user) }
membership.organization { Organization.first || Factory(:organization) }
end
FactoryGirl 4+ update:
Factory.define do
factory :membership do
user { create(:user) }
organization { Organization.first || create(:organization) }
end
end
Another approach is to use unique identifiers (e.g.: names) for each factory that you want to reuse, then use initialize_with to generate it:
factory :organization_1 do
ignore { organization_name 'Sample Organization 1' }
name { organization_name }
initialize_with { Organization.find_or_create_by_name(organization_name) }
end
Now any reference to :organization_1 will always retrieve the same Organization. Obviously, you must use distinct names for this to work.
There are two things.
1. You might still want to create unique names for Factory(:organisation) you can achieve that using Factory.sequence which will generate it uniquely for you.
2. You can pass in a Factory(:membership_request, :organization => #organization) to use the existing object instead of creating a new one.
With mongoid you can take combine the use of the #find_or_create_by method with Factory.attributes_for and do something like this
factory :membership do
organization { Organization.find_or_create_by(Factory.attributes_for(:organization))}
end
I'm sure ActiveRecord has something similar.