Epigene
Epigene

Reputation: 3928

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  

Upvotes: 2

Views: 579

Answers (2)

Epigene
Epigene

Reputation: 3928

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

Upvotes: 2

ehoffmann
ehoffmann

Reputation: 798

Why not doing this:

element = Element.find_by(name: "hydrogen") || FactoryGirl.create(:element, name: "hydrogen", symbol: "H")

Upvotes: 0

Related Questions