Search code examples
testingruby-on-rails-4factory-botrspec-rails

RSpec and FactoryGirl - building unique assocations?


A feedback model requires an account to exist. Each account has a unique :uuid that is validated in the model: validates :uuid, presence: true, uniqueness: true

I was using rspec to test the feedback model and kept getting Uuid has already been taken errors whenever I built a feedback object because feedback was building an associated account

spec/models/feedback_spec.rb

require "spec_helper"

describe Feedback do
  it "is valid with a message" do
    expect(build(:feedback)).to be_valid
  end

  it "is invalid without a message" do
    expect(build(:feedback, message: nil)).to have(1).errors_on(:message)
  end

  it "is invalid without an associated account" do
    expect(build(:feedback, account: nil)).to have(1).errors_on(:account)
  end
end

spec/factories/feedbacks.rb

FactoryGirl.define do
  factory :feedback do
    association :account
    message Faker::Lorem.paragraphs
  end
end

spec/factories/accounts.rb

FactoryGirl.define do
  factory :account do
    # uuid SecureRandom.uuid # THIS LINE WILL NOT WORK
    sequence(:uuid) { |n| n } # THIS LINE WORKS
    active true
  end
end

As you can see from the accounts.rb factory, the line uuid SecureRandom.uuid always seemed to return the same UUID when I created an account in the feedback_spec.rb file.

I'm wondering, does FactoryGirl only build the account object ONCE and then re-use it for each individual test? I was expecting a unique UUID for each account object but that obviously didn't work. However, when I switched to using the sequence(:uuid) { |n| n } line it worked just fine.

Trying to understand how FactoryGirl creates these objects for testing. It's obviously not the way I was thinking. My tests now run properly, but things that I don't understand will eventually come back to haunt me.


Solution

  • Try this:

    FactoryGirl.define do
      factory :account do
        uuid { SecureRandom.uuid }
        active true
      end
    end
    

    This will evaluate SecureRandom.uuid whenever an account factory is invoked instead of just the once when FactoryGirl defines it's factories.

    Edit:

    See Factory Girl Lazy Attributes for more info.