Factory Girl协会创建新工厂模型,而不是与现有模型相关联

时间:2016-12-14 18:45:46

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

错误消息:

 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)。我应该如何更改此代码   不要再次创建相同的工厂模型,而是将其与   现有的?   我使用Database Cleaner

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

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。 Rails 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)。我应该如何更改此代码   不要再次创建相同的工厂模型,而是将其与   现有的?

1 个答案:

答案 0 :(得分:0)

简答:

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

长答案:

让我们再看看错误。

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

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

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

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