FactoryGirl 创建重复实例

FactoryGirl to create duplicate instance

我想在 city 模型上测试条目的唯一性。

class City < ActiveRecord::Base
  validates :name, uniqueness: { scope: :country_code,
    message: "This city has already been added" }
end

我需要创建一个规范来测试此验证。我的想法是测试将沿着这些方向进行:

my_city = FactoryGirl.create(:city)

context "when inserting" do
  it "cannot have the same name and country code" do
    expect{create :my_city}.to raise_error(ActiveRecord::DuplicateRecord)
  end
end

但是,我一直无法弄清楚如何使用 FactoryGirl 创建 my_city 对象的重复实例。如上面代码片段所示,简单地使用 create :my_city 会导致:

ArgumentError: Factory not registered: my_city

编辑

这是我的 City 工厂的样子:

# spec/factories/city_factory.rb
FactoryGirl.define do
  factory :city do
    name { Faker::Address.city}
    country { CountryCodes.find(Faker::Address.country_code)}
    latitude { Faker::Number.decimal(2, 6)}
    longitude { Faker::Number.decimal(2, 6)}
    population { Faker::Number.number([3, 6, 7].sample) }
    timezone { Faker::Address.time_zone }
  end
end

这么简单运行create :city两次,会导致两个完全不同的城市被插入。我需要测试同一个城市,两次。

您可以像这样测试您的验证:

RSpec.describe City do
  let(:city) { create :city }
  subject    { city }

  it { should validate_uniqueness_of(:name).scoped_to(:country_code) }
end

但首先您需要像这样为 City 建立工厂(在单独的文件中):

FactoryGirl.define do
  factory :city do
    sequence(:name) { |n| Faker::Lorem.word + "(#{n})" }
  end
end

好吧,我无法用比错误告诉你更好的词来形容它了。您没有注册名为 my_city 的工厂。如果您想创建重复记录,只需调用 create 方法两次并传递您想要测试的重复属性。例如,您可以通过以下方式对此进行测试:

it "cannot have the same name and country code" do
  first_city = create :city
  duplicate_city = build(:city, name: first_city.name, country_code: first_city.country_code)
  expect(duplicate_city).not_to be_valid
end


it "cannot have the same name and country code" do
  attrs = FactoryGirl.attributes_for :city
  first_city = create :city, attrs
  duplicate_city = build(:city, attrs)
  expect(duplicate_city).not_to be_valid
end


it "cannot have the same name and country code" do
  first_city = create :city
  expect{create (:city, name: first_city.name, country_code: first_city.country_code)}.to raise_error(ActiveRecord::DuplicateRecord)
end

请注意,每次调用 create 方法时,都会创建一条新记录,因此第二次调用它时,数据库中已经存在一条记录,并且唯一的属性是不同的是id。例外情况是,如果您在工厂中声明了 sequence 或在测试执行时会发生变化的其他方法调用,例如 Date.today

一般来说,测试唯一性非常简单。让 shoulda matchers 为您完成这项工作。一旦你有了工厂,你就可以这样做...

specify { 
  create :city; 
  is_expected.to validate_uniqueness_of(:name).scoped_to(:country_code) }

您必须至少创建一条记录。然后 shoulda 匹配器将使用该记录,读取其属性,并制作副本以测试您的验证。

如果您不想使用 shoulda-matchers,您可以简单地使用 FactoryGirl 创建第一个城市,然后使用相同的 namecountry_code。最后,测试它在检查有效性时产生的错误信息:

city = create(:city)
invalid_city = City.new(country_code: city.country_code, name: city.name)


expect(invalid_city).not_to be_valid
expect(invalid_city.errors.full_messages).to include "This city has already been added"