Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FactoryBot and Faker - unique is not working

I'm using FactoryBot and Faker for my tests, and it looks like that Faker is generating the same name:

class Profile < ApplicationRecord
  belongs_to :user
  validates_presence_of :first_name, :last_name, :nickname
  validates :nickname, uniqueness: { case_sensitive: false }
end

FactoryBot.define do
  factory :user do
    sequence(:email) { |n| "user#{n}@example.org" }
    password "123456"
    trait :with_profile do
      profile
    end
  end
end

FactoryBot.define do
  factory :profile do
    first_name Faker::Name.unique.first_name
    last_name Faker::Name.unique.last_name
    nickname { "#{first_name}_#{last_name}".downcase }
    user
  end
end

RSpec.feature "Friendships", type: :feature do
  scenario "User can accept a pending friendship request" do
    @tom   = create(:user, :with_profile)
    @jerry = create(:user, :with_profile)
    #other stuff
  end
end

Even I'm using the unique method, I'm getting the error

ActiveRecord::RecordInvalid: Validation failed: Nickname has already been taken`.

Any clues?

like image 825
sekmo Avatar asked May 19 '18 17:05

sekmo


1 Answers

Should be:

first_name { Faker::Name.unique.first_name }
last_name { Faker::Name.unique.last_name }

When loading Faker::Name.unique.first_name will be evaluated. Threfore, use blocks.

Edit:

FactoryBot.define do
  factory :profile do
    first_name Faker::Name.unique.first_name
  end
end

In this example Faker::Name.unique.first_name will be evaluated once, during the factory definition (when the file is loaded/required). If it finds a unique value, say 'John Doe' it's will be used for each item created by this factory.

Or in other words: after the file is loaded, and Faker::Name.unique.first_name evaluated you may think of this factory as if it was:

FactoryBot.define do
  factory :profile do
    first_name 'John Doe'
  end
end

When you use blocks - the block's body will be evaluated each time you call create(:profile) or build(:profile). The Faker::Name.unique.first_name part inside the block will be called each time, and return different, unique results.

like image 71
yzalavin Avatar answered Sep 29 '22 04:09

yzalavin