How do I create an admin role with Factory girl? - ruby-on-rails

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.

Related

Factory girl association self referencing the parent model

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

Factory Girl rspec issue: has_many through factory

I am having an issue regarding Factory Girl in Rails. I currently have pundit setup am trying to test my user policies yet the factory does not seem to work when in rspec.
Inside the rails console, I can load up the console and type:
user=FactoryGirl.create(:admin_user)
r=user.roles
This works correctly creates a user and the correct associations between a user and a role. However, when the factory is used in rspec- A user is created but not the associated assignment. (I discovered this by using 'pp' inside the specific tests.)
I do not need to create a role, since the roles are set so I am looking them up.
Any ideas?
Models
class User < ApplicationRecord
has_many :assignments, dependent: :destroy
has_many :roles, through: :assignments, dependent: :destroy
def has_role?(roles)
roles.each do |role|
if self.roles.include? role
return true
end
end
false
end
class Role < ApplicationRecord
# Associations
has_many :assignments
has_many :users, through: :assignments
class Assignment < ApplicationRecord
# Associations
belongs_to :user
belongs_to :role
Factories
FactoryGirl.define do
factory :user do
first_name {Faker::Name.first_name}
last_name {Faker::Name.last_name}
email {Faker::Internet.email}
password {Faker::Internet.password(8)}
factory :admin_user do
after(:create) do |user|
Assignment.create(user: user , role: Role.find_by(label:'System Admin') )
end
end
end
end
Tests
User Policy Test
describe UserPolicy do
subject { UserPolicy }
let (:current_user) { FactoryGirl.build_stubbed :user}
let (:other_user) { FactoryGirl.build_stubbed :user }
let (:admin) { FactoryGirl.build_stubbed :admin_user}
permissions :index? do
it "denies access if not an admin" do
expect(UserPolicy).not_to permit(current_user)
end
it "allows access for an admin" do
expect(UserPolicy).to permit(admin)
end
end
end
Other Test With same Issue
feature 'User index page', :devise do
after(:each) do
Warden.test_reset!
end
scenario 'user sees own email address' do
user = FactoryGirl.create(:admin_user)
expect(user.has_role?(Role.where(label: 'System Admin'))).to eq true
login_as(user, scope: :user)
visit users_path
expect(page).to have_content user.email
end
end
This test fails since the user has no role assigned.
Controller
class AssignmentsController < ApplicationController
def create
#assignment = Assignment.new(assignment_params)
if #assignment.save
redirect_to users_path(), :notice => "Role Added"
else
flash[:alert]="Unable to Add Role"
end
end

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 call factory method in other file

I am using FactoryGirl in my rails application. I have following code snippet
FactoryGirl.define do
factory :pub_company, :class => Company do |c|
c.name "Dixons Group"
c.address "1500 Martin Ave"
c.city "Santa Clara"
c.state "CA"
end
end
FactoryGirl.buid(:pub_company)
Above code can creates a record fine. I am trying to create more data as similar to above one. so, i am using
require 'factory_girl_rails'
FactoryGirl.define do
factory :new do
FactoryGirl.create(:pub_company)
FactoryGirl.create(:adv_user)
FactoryGirl.create(:adv_setting)
FactoryGirl.create(:adv_ad)
end
end
it fails for me. Is this possible using factorygirl to call a different factory and create a record?
Since, the FactoryGirl is used mostly for testing models, I show the sample models, and factories, spec for the new model.
app/models/new.rb
class New < ActiveRecord::Base
attr_protected :company_id
belongs_to :company
end
app/models/company.rb
class Company < ActiveRecord::Base
attr_accessible :name, :address, :city, :state
has_many :news
end
spec/factories/company_factory.rb
FactoryGirl.define do
factory :company do |c|
c.name "Dixons Group"
c.address "1500 Martin Ave"
c.city "Santa Clara"
c.state "CA"
end
end
spec/factories/new_factory.rb
FactoryGirl.define do
factory :new do
company { FactoryGirl.create :company }
# adv_user { FactoryGirl.create :adv_user }
# adv_setting { FactoryGirl.create :adv_setting }
# adv_ad { FactoryGirl.create :adv_ad }
end
end
NOTE: You shell to have adv_user, adv_setting, adv_ad defined also.
spec/models/new_spec.rb
describe New do
it 'Test new model' do
new_instance = FactoryGirl.create :new
new_instance.pub_company.name.should == "Dixons Group"
...
end
end

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