Add (has_many) association to FactoryGirl.create? - ruby-on-rails

I know how to create a has_many associations when defining a Factory:
factory :user do
name "John Doe"
factory :user_with_posts do
ignore do
posts_count 5
end
after(:create) do |user, evaluator|
create_list(:post, evaluator.posts_count, user: user)
end
end
end
But how would I do that when I am actually creating the Factory, such as:
Factory.create(:user, :posts << ??)

One way is to use a block like this:
FactoryGirl.create(:user) do |user|
FactoryGirl.create_list(:post, 10, user: user)
end

Related

Has_many Association in FactoryGirl

I have 2 classes Users and Authentications, then Authentications has_many Users:
User class:
FactoryGirl.define do
factory :user do
first_name "Juan"
last_name "Iturralde"
sequence(:email) { |n| "person-#{n}#example.org" }
password "1234567890"
password_confirmation "1234567890"
is_admin false
factory :admin do
is_admin true
end
after(:create) do |user|
create(:authentication, user: user)
end
end
end
Authentication class:
FactoryGirl.define do
factory :authentication do
user {User.first || create(:user)}
provider "Apple"
uid "uid"
end
end
And i dont now. How create an user in authentication?
To build associations in specs I use the following:
# spec/factories/posts.rb
FactoryGirl.define do
factory :post do
title 'The best post'
trait :with_comments do
create_list :comment, 3
end
end
end
# spec/factories/comments.rb
FactoryGirl.define do
factory :comment do
post
content 'Really awesome post'
end
end
# in specs
. . .
let(:post_with_commments) { create :post, :with_comments }
. . .

FactoryGirl How to add several objects with different roles

class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.has_role? :student
can :create, Atendimento
end
if user.has_role? :professor
can :create, Atendimento
end
if user.has_role? :administrative
can [:read, :create], [Atendimento]
can [:edit, :update], Atendimento
can :manage, [Type, Place]
end
if user.has_role? :admin
can :manage, :all
end
end
end
and my factory
FactoryGirl.define do
factory :user do |f|
f.name "Alessandro"
f.username "alessandrocb"
f.matricula "123456789"
f.password "123456789"
f.password_confirmation "123456789"
f.after(:create) {|user| user.add_role(:student)}
end
I need those mocks receive all roles , but now I can only student role
my test with rspec
subject(:ability){ Ability.new(user) }
let(:user){ nil }
context "when is an User" do
let(:user) { FactoryGirl.create(:user) }
what is happening is this: I can only test with rspec with only 1 paper, but would like to test with all the cancan, I need to create the factory with all these possibilities for different roles
First solution
FactoryGirl.define do
factory :user do
name "Alessandro"
username "alessandrocb"
(...)
trait :student do
after(:create) {|user| user.add_role(:student)}
end
trait :professor do
after(:create) {|user| user.add_role(:professor)}
end
trait :administrative do
after(:create) {|user| user.add_role(:administrative)}
end
trait :admin do
after(:create) {|user| user.add_role(:admin)}
end
end
end
You can then use and combine these traits like this:
# Create a student
create :user, :student
# Create a user who is both professor and admin
create :user, :professor, :admin
Second solution
FactoryGirl.define do
factory :user do
name "Alessandro"
username "alessandrocb"
(...)
ignore do
role
end
after(:create) do |user, params|
user.add_role(params.role) if params.role
end
end
end
And then:
# Create a student
create :user, role: :student
Note that the second solution does not allow you to combine roles as it is. But you could use an array to achieve this.
I recently ran into a similar issue. Here's my users factory:
FactoryGirl.define do
sequence :email do |n|
"user#{n}#example.com"
end
factory :user do
email
password 'password'
factory :admin_user do
role 'administrator'
end
factory :support_user do
role 'support'
end
factory :editor_user do
role 'editor'
end
factory :sales_user do
role 'sales'
end
factory :author_user do
role 'author'
end
factory :guest_user do
role 'guest'
end
end
end
From there I can just call the relevant factory for a spec:
create(:editor_user)
Or, depending on your User model and it's attendant properties, you could also build factories like:
create(:user, role: 'guest') # my User model has a properly called 'role'
I have 3 different users in my project: default, merchant, admin.
I have one file that handles the conditions. Note: this is FactoryBot and specifically factory bot rails. I am also using the gem Faker.
edit: the numbered roles are using enum, which converts the number in a string according to an array I defined. More on enums: https://naturaily.com/blog/ruby-on-rails-enum
factories/user.rb
// factories/user.rb
FactoryBot.define do
factory :user do
name { Faker::Name.first_name }
street_address { Faker::Address.street_address }
city { Faker::Address.city }
state { Faker::Address.state }
zip { Faker::Address.zip }
email { Faker::Internet.email }
password { Faker::Internet.password }
trait :default_user do
role { 0 }
end
trait :admin_user do
role { 1 }
end
trait :merchant_user do
role { 2 }
end
end
end
spec file
// a spec file
RSpec.describe 'User logging in' do
let(:user) { create(:user, :default_user) }
let(:admin) { create(:user, :admin_user) }
[...]
end

how to create a factory for a relationship between two users

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)

Where do I confirm user created with FactoryGirl?

Using rails, devise, rspec & factorygirl:
Trying to create some tests for my site. I'm using the confirmable model for devise so when I create a user using FactoryGirl, the user isn't confirmed.
This is my factories.rb:
FactoryGirl.define do
factory :user do
full_name "Aren Admin"
email "aren#example.com"
password "arenaren"
password_confirmation "arenaren"
role_id ADMIN
end
end
And this is my rspec test file:
require 'spec_helper'
describe "Admin pages" do
subject { page }
describe "home page" do
let(:user) { FactoryGirl.create(:user) }
before { visit admin_home_path }
it { should have_content("#{ROLE_TYPES[user.role_id]}") }
end
end
I'm getting an error because the user is not confirmed. By searching around I'm pretty sure I need to use the method 'confirm!' and that it belongs in the factories.rb file, but I'm not sure where to put it.
You could also set the confirmed_at attribute as follows. Works for me:
FactoryGirl.define do
factory :user do
full_name "Aren Admin"
email "aren#example.com"
password "arenaren"
password_confirmation "arenaren"
role_id ADMIN
confirmed_at Time.now
end
end
Better yet, do the following (then you don't need to create a before filter for every test suite)
Factory.define :confirmed_user, :parent => :user do |f|
f.after_create { |user| user.confirm! }
end
found here:
https://stackoverflow.com/a/4770075/1153149
Edit to add non-deprecated syntax
FactoryGirl.define do |f|
#Other factory definitions
factory :confirmed_user, :parent => :user do
after_create { |user| user.confirm! }
end
end
Edit 01/27 To Update Syntax Again
FactoryGirl.define do
#Other factory definitions
factory :confirmed_user, :parent => :user do
after(:create) { |user| user.confirm! }
end
end
Try user.confirm! in your before block
found here
This is the factory that worked for me
FactoryGirl.define do
factory :user do
sequence :email do |n|
"address#{n}#example.com"
end
sequence :password do |n|
"password#{n}"
end
factory :confirmed_user do
before(:create) {|user| user.skip_confirmation! }
end
end
end
Put the Devise confirmable logic in the after(:build) callback...
FactoryGirl.define do
factory :user do
after(:build) do |u|
u.confirm!
u.skip_confirmation_notification!
end
...
end
For me, putting confirm! or skip_confirmation! in the after(:create) block caused validation errors on the email parameter and did not work.
Add this line to your User factory definition:
before(:create) { |user| user.skip_confirmation! }
You should call skip_confirmation! before create so this is persisted on the user.
before(:create) do |user|
user.skip_confirmation!
end
2023
Does not work:
before(:create, &:skip_confirmation!)
Works:
after(:build, &:skip_confirmation!)

Rails 3 - Factory girl and sequence for belongs_to table

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)) )

Resources