factory girl rails traits for model associations - ruby-on-rails

I'm trying to setup my factory girl that when I passed in create(:user, :admin), it will create a user and add a record in the Role model/table. My association is that, User has_one Role, Role belongs_to User
This is the setup of my factory girl
FactoryGirl.define do
factory :user do
name "Test User"
email "test_user#serviceseeking.com.au"
contact_phone "0412345678"
password "123123"
password_confirmation "123123"
skip_email_validation true
trait :as_admin do
factory(:role) { name "Untouchable" }
end
end
end
But for some reason, it doesn't create the role.

What errors are you getting?
I think you should be getting an error saying that there is no trait called :admin. If you want to create admin users with a call to create(:user, :admin) your trait must be named :admin.
Secondly I also think that what you want to do inside the :admin trait is this:
trait :admin do
role name: "Untouchable"
end
If you have your association set up and a factory for Role called :role this should create a Role record with the name "Untouchable" and associate it with your user.
I hope this helps!

Related

FactoryGirl failed validation while creating with a trait that contains required attributes

I have a User model with two roles: member and admin and defaults to member. This model has two required attributes payout_bank and payout_account and they are only required to be present if the role is member.
My FactoryGirl for User has two traits that corresponds to the roles. I then add the two required attributes to the member trait, but when I create an User with member trait with linting turned on FactoryGirl will complain that the required attributes are failing validation because they're blank.
If I turn off linting it will correctly generate a member User with payout_bank and payout_account filled out. What's going on?
Model User.rb
class User < ActiveRecord::Base
validates_presence_of :payout_bank, :payout_account, if: :member?
def set_default_role
self.role ||= :member
end
end
Factory User.rb
FactoryGirl.define do
factory :user do
sequence(:email) { |n| "user#{n}#email.com" }
password "password"
trait :member do
sequence(:email) { |n| "member#{n}#email.com" }
role "member"
payout_bank { Faker::Company.name }
payout_account { Faker::Number.number(10) }
end
trait :admin do
sequence(:email) { |n| "admin#{n}#email.com" }
role "admin"
status "active"
end
end
end
Creating user from Factory
FactoryGirl.lint
FactoryGirl.create(:member_user, :member)
Output
FactoryGirl::InvalidFactoryError: The following factories are invalid:
* member_user - Validation Failed: payout_bank cannot be blank, payout_account cannot be blank (ActiveRecord::RecordInvalid)/usr/local/lib/ruby/gems/2.3.0/gems/factory_girl-4.7.0/lib/factory_girl/linter.rb:12:in `lint!'
You have to write the validation tests in spec/models directrory. Something like.
require 'rails_helper'
require "validates_email_format_of/rspec_matcher"
RSpec.describe BankAccount, type: :model do
it "should have a account number" do
bank_a = build(:bank_account, account_no: '')
expect(bank_a).to_not be_valid
end
end
I found out that FactoryGirl.lint is the line that blew up and caught the validation failure. The issue here is that linting will try to create generic User model with no traits. Since my User model defaults to member role, and this role requires payout_bank and payout_account to be present, the validation thus failed.
The solution is to move these two attributes out of the member trait and into the base in the factory.
Something like this
FactoryGirl.define do
factory :user do
sequence(:email) { |n| "user#{n}#email.com" }
password "password"
payout_bank { Faker::Company.name } # Move here
payout_account { Faker::Number.number(10) } # Move here
trait :member do
sequence(:email) { |n| "member#{n}#email.com" }
role "member"
end
trait :admin do
sequence(:email) { |n| "admin#{n}#email.com" }
role "admin"
status "active"
payout_bank nil
payout_account nil
end
end
end

Can't build has_many association with Factory Girl

I have a User that has_many posts in Rails 4.1.6. Following the Getting Started page for Factory Girl, I have created these factories:
factory :post do
skip_create
title 'foo bar'
user
end
factory :user do
skip_create
id 1
username 'alice'
factory :user_with_posts do
skip_create
transient do
posts_count 5
end
after(:build) do |user, evaluator|
build_list(:post, evaluator.posts_count, user: user)
end
end
end
But calling build(:user_with_posts) returns a User with an empty posts array. Calling build_list(:post, 5, user: user) (with a pre-built User) works.
What am I missing?
Also, is there a way to set a global skip_create so I don't have to set it on each factory?
I had a similar problem, and all according to all the docs I was reading, it should have been working. Here is what finally worked (adapted for your code):
change
build_list(:post, evaluator.posts_count, user: user)
to
user.posts = build_list(:post, evaluator.posts_count, user: user)
Let me know if that doesn't work, but that's what solved it for me.

How to fetch an object of a nested Factory in RSPEC/FactoryGirl

I create two models at the same time trough a nested Form and want to test the functionality with this factory:
factory :company do
name "ACME"
after(:build) do |company|
company.users << FactoryGirl.build(:user, company: company)
end
end
factory :user do
first_name "Foo"
last_name "Bar"
email "foo#bar.com"
password "foobar"
password_confirmation "foobar"
company
end
Now I create a company in RSPEC like
let(:company) { FactoryGirl.create(:company) }
and have a test like
it { should have_title(user.first_name) }
where i want to fetch the user's fisrt name from the user's model. Right now I can only access the Company's model in RSPEC.
How can I fetch the attributes of the user's model?
first you have to create user but you only build so without save:
company.users << FactoryGirl.create(:user)
or
FactoryGirl.create(:user, company: company)
next you can have access to user first name for example by(if company has_many :users):
it { should have_title(company.users.first.first_name) }

Change an attribute on a Factory after creation of record

Using FactoryBot, I'm having trouble creating a admin Factory in my specs because every user is assigned a default role of user in a before_create callback. This means that any role I assign a factory will be changed to user when the callback happens.
What I really want to do is something like this:
Inside my spec
admin = FactoryBot.create(:user)
admin.role = 'admin'
The second line, admin.role = 'admin' doesn't do anything. Any ideas?
I'm open to better ways of doing this as well.
There might be a way of reassigning the value to a FactoryBot (formerly FactoryGirl) instantiation, but RSpec negates the need:
describe User do
let(:user) { FactoryBot.create(:user) }
context 'when admin' do
let(:user) { FactoryBot.create(:user, admin: true) }
# ...
end
end
Try using a trait:
factory :user do
sequence(:username) { |n| "User ##{n}"}
role 'user'
trait :is_admin do
role 'admin'
end
end
Usage:
FactoryBot.create(:user, :is_admin)
Or eventually an after(:create):
factory :user do
sequence(:username) { |n| "user ##{n}"}
role 'user'
end
factory :user_admin, class: User do
after(:create) { |user| user.role = 'admin'; user.save } # don't know if the .save is necessary here
sequence(:username) { |n| "User Admin ##{n}"}
end
Just another way
# Steal some code from MrYoshiji at first.
factory :user do
sequence(:username) { |n| "User ##{n}"}
role 'user'
# Then a separate factory inside
factory :admin do
role 'admin'
end
end
# Use
FactoryBot.create(:admin)

Specifying CanCan limitation based on an association

My User model has the following action:
def release(idea)
if idea.status == "claimed"
idea.status = "available"
self.ideas.delete(idea)
end
end
Each user has_many ideas, and this is the way to release an idea and say "I don't want to be responsible for this idea anymore."
However, the current implementation lets one user release an idea owned by another user. I could easily solve this in the function itself by checking for idea.user_id but I'm trying to learn how to use CanCan and rolify... which is where the problem starts.
ability.rb says:
if user.has_role? :user
can :release, Idea, user_id: user.id
end
This fails in rspec:
Failures:
1) User manipulates ideas: can't change an idea he doesn't own
Failure/Error: let(:james) {create(:user)}
NoMethodError:
undefined method `find_or_create_by' for #<Rolify::Adapter::ResourceAdapter:0xabc7d98>
tl;dr: Should I be specifying abilities for User or for Idea? If User, how should I word the condition?
Thank you!
Edit: User factory:
FactoryGirl.define do
factory :user do
name Faker::Name.name
email Faker::Internet.email
provider "MyString"
uid {"user_#{rand(1000).to_s}" }
trait :admin do
after(:create) {|user| user.add_role(:admin)}
end
trait :guest do
after(:create) {|user| user.add_role(:guest)}
end
trait :authorized_user do
after(:create) do |user|
user.add_role(:user)
user.remove_role(:guest)
end
end
end
end

Resources