FactoryGirl How to add several objects with different roles - ruby-on-rails

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

Related

FactoryGirl::AttributeDefinitionError: Attribute already defined: user

I created factory client and contract. I run test, but display error
FactoryGirl.define do
factory :client, class: User do
role 'client'
first_name 'John'
sequence(:last_name) { |n| "client#{n}" }
sequence(:email) { |n| "client#{n}#example.com" }
# avatar { Rack::Test::UploadedFile.new(File.join(Rails.root, 'public', 'images', '128.jpg')) }
password 'password'
password_confirmation 'password'
end
end
support/controller_macros.rb
module ControllerMacros
def login_client
before do
#client = create(:client)
##request.env['devise.mapping'] = Devise.mappings[:client]
sign_in #client
end
end
end
FactoryGirl.define do
factory :contract do
sequence(:title) { |n| "translation#{n}" }
amount 150
additional_information 'X' * 500
due_date { 21.days.from_now }
association :user, factory: :client
association :user, factory: :contractor
end
end
I run test
rspec spec/controllers/contracts_controller_spec.rb
require 'rails_helper'
describe ContractsController do
login_client
let(:contract) { create(:contract) }
describe 'POST #create' do
context 'with valid attributes' do
it 'redirects to payment page' do
post :create, contract: attributes_for(:contract)
expect(response).to redirect_to payment_new_path
end
end
end
end
Error display:
Failure/Error: post :create, contract: attributes_for(:contract)
FactoryGirl::AttributeDefinitionError:
Attribute already defined: user
What is wrong in factory or test?
Factory :contract defines two attributes named user, which isn't allowed.
Give them unique (within the factory) labels, e.g.:
FactoryGirl.define do
factory :contract do
sequence(:title) { |n| "translation#{n}" }
amount 150
additional_information 'X' * 500
due_date { 21.days.from_now }
association :client, factory: :client
association :contractor, factory: :contractor
end
end
As they seem fitting, I've chosen attribute names corresponding with the factory names. This allows to even shorten this, by leaving out the factory name:
FactoryGirl.define do
factory :contract do
sequence(:title) { |n| "translation#{n}" }
amount 150
additional_information 'X' * 500
due_date { 21.days.from_now }
client
contractor
end
end
(See http://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md, section "Associations":
If the factory name is the same as the association name, the factory name can be left out.
)

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 }
. . .

Add (has_many) association to FactoryGirl.create?

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

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

How do I create an admin role with Factory girl?

Ok so i want to test out features that need an admin user and i am trying to login as admin but in order to do that i need to have an admin user
here is my code
let(:user) { FactoryGirl.create(:user) }
let(:admin_role) { FactoryGirl.create(:role) }
FactoryGirl.define do
factory :user do
first_name "John"
last_name "Doe"
email "john#doe.com"
end
factory :role do
name "Admin"
end
end
how do i connect them I tried user.roles << user_role but got this error
/Users/matt/Sites/application/spec/controllers/directory_controller_spec.rb:16:in `block (3 levels) in <top (required)>': undefined local variable or method `user' for #<Class:0x007fa550890d80> (NameError)
Here is my models
class User < ActiveRecord::Base
has_many :roles, :through => :role_users
has_many :role_users
...
class Role < ActiveRecord::Base
has_many :users, :through => :role_users
has_many :role_users
...
class RoleUser < ActiveRecord::Base
belongs_to :role
belongs_to :user
end
spec/factories/user.rb
FactoryGirl.define do
factory :user do
first_name "John"
last_name "Doe"
email "john#doe.com"
end
factory :admin_user, :parent => :user do
roles { [ FactoryGirl.create(:admin_role) ] }
end
factory :role do
name { "Role_#{rand(9999)}" }
end
factory :admin_role, :parent => :role do
name "Admin"
end
end
Solved
Factory.sequence(:email) {|n| "person#{n}#example.com" }
Factory.define :role do |r|
r.name {"Admin"}
end
Factory.define :role_user do |ru|
ru.association(:role)
ru.association(:user)
end
Factory.define :user do |u|
u.first_name {"matt"}
u.last_name {"jones"}
u.email {Factory.next(:email)}
end
and using it like
let(:role_user) { FactoryGirl.create(:role_user) }
before { sign_in(role_user) }
I was having the exact same problem, with code like:
describe "..." do
let(:user) { create :user }
let(:manager) { create :manager_role }
user.roles << manager
it "should ...." do
# etc.
end
end
# run rspec...
# => `block (2 levels) in <top (required)>': undefined local variable or method `user' for #<Class:0x000001016a2dc0> (NameError)
When I moved the user.roles << manager code into a before(:each) block, the errors disappeared. Thus, something like:
describe "..." do
let(:user) { create :user }
let(:manager) { create :manager_role }
before(:each) do
user.roles << manager
end
it "should ...." do
# etc.
end
end
This saves the pain of having to specify the role <-> user association in your factories.
I'm not familiar enough with how rspec and factory_girl work to understand why you can't have code like user.roles << role immediately after calling let(:user) { create :user }, but putting it into a before(:each) block or into an individual it "should ... " do block should do the trick.

Resources