I'm trying to instanciate with FactoryGirl multiple object (guests who have unique email)
guests = create_list(:guest, 3)
But I get error :
Failure/Error: guests = create_list(:guest, 3)
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
Here my factory :
FactoryGirl.define do
factory :guest do
email Faker::Internet.unique.email
firstname 'John'
lastname 'Doe'
password 'test'
password_confirmation 'test'
factory :guest_with_bookings do
email "#{SecureRandom.hex}#test.com"
transient do
bookings_count 2
end
after(:create) do |guest, evaluator|
create_list(:booking, evaluator.bookings_count, guests: [guest])
end
end
end
end
Even with Faker::Internet.unique.email, it's don't working when I try to create more than 1 guest
Use sequence in your factory
FactoryGirl.define do
factory :guest do
sequence(:email) { |n| "name_#{n}#example.com" }
Or in combination with Faker
FactoryGirl.define do
factory :guest do
sequence(:email) { |n| Faker::Internet.unique.email("Nancy_#{n}") }
Related
I have a user factory that be default creates an association for user_document
FactoryGirl.define do
factory :user do |u|
sequence(:first_name) {|n| "firstname-#{n}" }
after(:create) do |user|
create(:user_document, document_type: :address_proof, user: user)
end
end
end
I'd like to define a new factory for which the association user_document is null. How do I do this ?
[UPDATE]
When I do this
factory :user_with_no_doc_buyer do
user_document nil
end
I get an error saying :
NoMethodError: undefined method `user_document=' for #<User:0x007f97329c08f8>
and When I do
user_documents nil I get
NoMethodError: undefined method `each' for nil:NilClass
thanks
I'd create two traits.
FactoryGirl.define do
factory :user do
sequence(:first_name) {|n| "firstname-#{n}" }
trait :with_document do
after(:create) do |user|
create(:user_document, document_type: :address_proof, user: user)
end
end
trait :without_document do
user_documents []
end
end
end
Then you just call whichever factory you need.
FactoryGirl.create(:user, :with_document) for example
Edit: Have seen you want the with document to be the default. That's easy enough. You can just use the code you have and take my suggestion of a trait without document which you can call when you need it.
Use traits
FactoryGirl.define do
factory :user do |u|
sequence(:first_name) {|n| "firstname-#{n}" }
trait :with_user_document
after(:create) do |user|
create(:user_document, document_type: :address_proof, user: user)
end
end
end
end
To create user with document
FactoryGirl.create(:user, :with_user_document)
To create user with null document
FactoryGirl.create(:user)
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
I've just started using FactoryGirl for my tests, and I've run into some issues.
I have user, qualification and user_qualification models. For qualifications, I have two inherited factories, namely qualification_ba and qualification_phd. Now, I'm trying to generate a qualification that includes either qualification_ba or qualification_phd factories by running:
create(:user_qualification_ba_bad_marks).
This returns:
NoMethodError: undefined method 'qualification_ba='.
However, when running:
create(:qualification_ba)
it successfully creates the object as expected. Is there perhaps a limit on how deep you can nest factories?
factory :user do
to_create {|instance| instance.save(validate: false) }
full_name Faker::Name.name
email Faker::Internet.email
password Faker::Internet.password(8)
factory :user_ba_candidate do
before(:create) do |user|
create_list(:user_qualification_ba_bad_marks, 1, user: user)
end
end
factory :user_phd_candidate do
before(:create) do |user|
create_list(:user_qualification_phd_good_marks, 1, user: user )
end
end
end
factory :qualification do
user_selectable true
factory :qualification_ba do
name "Bachelors"
qualification_level 2
end
factory :qualification_phd do
name "Masters"
qualification_level 5
end
end
factory :user_qualification do
to_create {|instance| instance.save(validate: false) }
factory :user_qualification_ba_bad_marks do
qualification_ba
aggregate (30..50).to_a.sample
end
factory :user_qualification_phd_good_marks do
qualification_phd
aggregate (70..90).to_a.sample
end
user
end
I have a question in Rails 4.
On my factories.rb i have something like:
factory :user do
sequence(:name) { |n| "User #{n}" }
sequence(:email) { |n| "user_#{n}#example.org" }
password 'foobar'
password_confirmation 'foobar'
end
The problem, i don't know to add a relation after created user.
When i create a user, and he is a moderator i assign to user the role like that:
u.add_role_id!(11)
I have different table for relationships, with user_id and role_id and on user model i have the method.
def add_role_id!(role)
role_user_relationship.create!(role_id: Role.find(role).id)
end
How i do that on factories.rb?
Now what i now to do:
u = FactoryGirl.create(:moderator)
u.add_role_id!(11)
I want to assign by default on factories.rb
How I do that?!
Thanks everybody!
Manu
Solution:
I have to add these lines:
after(:create) do |u|
u.add_roles_ids!([10,20,30])
end
With the entire code:
factory :user do
sequence(:name) { |n| "User #{n}" }
sequence(:email) { |n| "user_#{n}#mudev.org" }
password 'foobar'
password_confirmation 'foobar'
after(:create) do |u|
u.add_roles_ids!([10,20,30])
end
end
Thanks to Peter Alfvin
You can use FactoryGirl's after callback mechanism described at https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#callbacks to get the comparable behavior to what you're doing now by including the following within your factory definition:
after(:create) {|u| u.add_role_id!(11)
Depending on your validation, you can alternatively do this after(:build) or before(:create).
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) }