Search code examples
ruby-on-rails-4rspecfactory-bot

How to return an existing object with FactoryGirl


For the sake of example, I have a model for chemical elements

class Element < ActiveRecord::Base
  validates :name, presence: true, uniqueness: true
  validates :symbol, presence: true, uniqueness: true 
end

In seeds.rb I define all the known elements

Element.where(name: "hydrogen" symbol: "H").first_or_create
...
Element.where(name: "ununoctium" symbol: "Uuo").first_or_create

I define a factory

FactoryGirl.define do
  factory :element do
    name "hydrogen" 
    symbol "H"
  end
end

Since the codebease depends on the presence of all seed elements, I prepare the test database with

rake db:seed RAILS_ENV=test

Now my factory will never work

FactoryGirl.create(:element) #=> validation failed error

How do I write a factory in a way that first tries to find the creatable object in the database?

FactoryGirl.create(:element) #=> returns Element from db || Factory invocation  

Solution

  • For sake of posterity, answering my own question with half a decade of experience under the belt.

    Try using FactoryBot's initialize_with hook as described in docs.

    factory :element do
      initialize_with do
        where(name: name, symbol: symbol).first_or_initialize
      end
    
      name "hydrogen" 
      symbol "H"
    end
    

    A spin on the idea is to forego the general factory and only have named factories for countable, uniqueness-constrained resources like chemical elements:

    factory :element_hydrogen do
      initialize_with do
        where(name: "hydrogen", symbol: "H").first_or_initialize
      end
    end