find_or_initialize_by in FactoryGirl

那年仲夏 提交于 2019-12-24 11:27:52

问题


I was wondering if there's an equivalent for find_or_initialize_by in FactoryGirl that solve teh following issue:

The objective is that the model uses two tables that have the same country. I don't want to use a sequence for the country (as I found for Emails).

There's a uniqueness constraint on Country, but my main issue is that it create twice the same record of Country when I call once FactoryGirl.create(:click)

Thus, the Validation fail in the test.

Rspec:

# models/click_spec.rb
describe Click do
    it "should have a valid constructor" do
        FactoryGirl.create(:click).should be_valid
    end
end

Factories:

# factories/countries.rb
FactoryGirl.define do
    factory :country do
        name "United States"
        slug "us"
    end
end

# factories/offers.rb
FactoryGirl.define do
    factory :offer do
        association :country, factory: :country
        # Other columns
    end
end

# factories/users.rb
FactoryGirl.define do
    factory :user do
        association :country, factory: :country
        # Other columns
    end
end

# factories/clicks.rb
FactoryGirl.define do
    factory :click do
        association :offer, factory: :offer
        association :user, factory: :user
        # Other columns
    end
end

Model:

class Country < ActiveRecord::Base
    validates :name, :slug,
    presence: true,
    uniqueness: { case_sensitive: false }

    validates :slug,
    length: { is: 2 }

end

回答1:


You should be able to make this work by using initialize_with:

FactoryGirl.define do
  factory :country do
    name "United States"
    slug "us"
    initialize_with { Country.find_or_create_by_name(name) }
  end
end

This will always use the same country. You may want to nest the factory to allow other factories to use different names:

FactoryGirl.define do
  factory :country do
    initialize_with { Country.find_or_create_by_name(name) }
    factory :united_states do
      name "United States"
      slug "us"
    end
  end
end



回答2:


I faced similar issues, also with the Country model of my application. Here's what I did.

To ensure FactoryBot's build and create still behaves as it should, we should only override the logic of to_create, by doing:

factory :country do
  to_create do |instance|
    instance.attributes = Country.create_with(name: instance.name).find_or_create_by(slug: instance.slug).attributes
    instance.reload
  end

  name { "United States" }
  slug { "us" }
end

Query explained:

Country
.create_with(name: instance.name) # if not found, create with this `name` (and `slug` defined below)
.find_or_create_by(slug: instance.slug) # find by primary key `slug'

This ensures build maintains it's default behavior of "building/initializing the object" and does not perform any database read or write so it's always fast. Only logic of create is overridden to fetch an existing record if exists, instead of attempting to always create a new record.

Originally posted on https://stackoverflow.com/a/55235861/3956879.

Check out my article explaining this.



来源:https://stackoverflow.com/questions/22874292/find-or-initialize-by-in-factorygirl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!