【问题标题】:Factory Girl associations creates new factory model instead of associate to existing one工厂女孩协会创建新的工厂模型,而不是与现有模型相关联
【发布时间】:2026-02-23 01:15:02
【问题描述】:

错误信息:

 Failure/Error: let(:rubric_in_grenoble){ create(:rubric_in_grenoble) }

     ActiveRecord::RecordInvalid:
       Validation failed: Name has already been taken

所以,正如我想的那样,尝试创建第二个 :rubric 工厂 时间(因为 :rubric_in_grenoble 应该与 与 :rubric_in_wroclaw 相同的文化)。我应该如何将此代码更改为 不再创建相同的工厂模型,而是将其与 现有的? 我使用数据库清理器

我正在为模型人口编写测试。

class Population < ApplicationRecord
  belongs_to :province
  belongs_to :culture

  validates_presence_of :province, :culture

  #some methods
end

与省份相关

class Province < ApplicationRecord
  enum terrain: [:sea, :plains, :hills, :mountains]
  validates :terrain, presence: true
  validates :name,
            presence: true,
            uniqueness: true,
            format: {
                with: /\A[A-Za-z ]{3,30}\z/,
                message: "province name has to contain letters and spaces only"
  }
  has_many :populations

 ##some methods
end

还有文化

class Culture < ApplicationRecord
  before_validation :downcase_name
  validates :name, presence: true,
            format: {
                with: /\A[a-z]{3,20}\z/,
                message: "culture name has to contain letters only"
            },
            length: { minimum: 3, maximum: 20 }

  ##many-to-many associations to different model - tested correctly

  has_many :populations

end

在我的测试套件中,我使用 FactoryGirl 和 RSpec。导轨 5.0.1。我还安装了 simplecov(我 99.99% 确定它没有干扰,但最好突出显示这一点)并且 DatabaseCleaner 工作正常。

文化和省份模型已经过测试,一切正常。在我的 FactoryGirl 人口文件中有:

FactoryGirl.define do
  factory :rubric_in_wroclaw, class: "Population" do
    association :province, factory: :wroclaw
    association :culture, factory: :rubric
    quantity 10
  end

  factory :javan_in_wroclaw, class: 'Population' do
    association :province, factory: :grenoble
    association :culture, factory: :javan
    quantity 15
  end

  factory :rubric_in_grenoble, class: 'Population' do
    association :province, factory: :grenoble
    association :culture, factory: :rubric
    quantity 25
  end
end

在我的 population_spec.rb 文件中,我有:

require 'rails_helper'

RSpec.describe Population, type: :model do

  let(:rubric_in_wroclaw){ create(:rubric_in_wroclaw) }
  let(:javan_in_wroclaw){ create(:javan_in_wroclaw) }
  let(:rubric_in_grenoble){ create(:rubric_in_grenoble) }
  let(:pops){ [ rubric_in_wroclaw, javan_in_wroclaw, rubric_in_grenoble] }



  describe '#validations' #shoulda-matchers validations - GREEN

  describe '#methods' do
    describe '#global_population' do
      context 'without any pop' #without any fixture created
      context 'with many pops' do
        before { pops } <--- there is an error

        it 'is equal to 50' do
          expect(Population.global_population).to eq 50
        end

      end
    end**


  end

  describe '#factories' #each factory is tested separately - everything's ok


end

重复的问题(因为帖子很长)

所以,正如我想的那样,尝试创建第二个 :rubric 工厂 时间(因为 :rubric_in_grenoble 应该与 与 :rubric_in_wroclaw 相同的文化)。我应该如何将此代码更改为 不再创建相同的工厂模型,而是将其与 现有的?

【问题讨论】:

    标签: ruby-on-rails rspec associations factory-bot


    【解决方案1】:

    简答:

    您可能为 Province.name 字段提供了重复值。 sequence 应该可以解决问题。

    长答案:

    让我们再看看错误。

     Failure/Error: let(:rubric_in_grenoble){ create(:rubric_in_grenoble) }
    
     ActiveRecord::RecordInvalid:
       Validation failed: Name has already been taken
    

    异常告诉我们我们正在尝试创建一个名称的记录两次 我可以在Province.name 中看到uniqueness 验证。

    class Province < ApplicationRecord
      enum terrain: [:sea, :plains, :hills, :mountains]
      validates :terrain, presence: true
      validates :name,
                presence: true,
                uniqueness: true,
                format: {
                    with: /\A[A-Za-z ]{3,30}\z/,
                    message: "province name has to contain letters and spaces only"
      }
      has_many :populations
    
     ##some methods
    end
    

    您没有粘贴 Province 工厂,但我希望工厂是这样的:

    FactoryGirl.define do
      factory :my_factory, class: "Province" do
        name 'blabla'
        # other fields
      end
    end
    

    所以我们必须使用序列而不是静态值。

    FactoryGirl.define do
      factory :grenoble, class: "Province" do
        sequence(:name) { |i| "BLABLA#{(i+64).chr}" }
        # other fields
      end
    end
    

    分配同一个省份

    RSpec.describe Population, type: :model do
    
      let(:my_province){ create(:my_province) }
      let(:rubric_in_wroclaw){ create(:rubric_in_wroclaw, province: :my_province) }
      let(:javan_in_wroclaw){ create(:javan_in_wroclaw, province: :my_province) }
    end
    

    【讨论】:

    • 是的,你对我的问题完全正确。但我需要解决方案,其中两个人口将与完全相同的省份相关联。谢谢:)。
    • @Milord 再次检查答案。