FactoryBot and Faker - unique is not working
Asked Answered
H

1

8

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?

Hophead answered 19/5, 2018 at 17:34 Comment(0)
S
15

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.

Suspensor answered 19/5, 2018 at 17:39 Comment(3)
Huge thanks! Could you just explain better why it wasn't working without the block?Hophead
FactoryBot separates attributes into static and dynamic. The best explanation of dynamic attributes is here.Suspensor
In te first example the Faker::Name.unique.first_name was evaluated once (during file loading) and each item created with this factory will get the (same) value it evaluated to. In the solution it is a block, and it's body is evaluated each time you create an item using this factory.Traveler

© 2022 - 2024 — McMap. All rights reserved.