【问题标题】:Rspec, Factory Girl build with has_manyRspec,Factory Girl 使用 has_many 构建
【发布时间】:2015-09-08 06:17:28
【问题描述】:

我有一个具有一对多关系的模型,它们是 Poll 和 Choice。

我如何正确测试它们,因为下面的代码导致ActiveRecord::RecordInvalid:。我想在旅途中创建父 (Poll) 及其子 (Choices),而不是先创建父 (Poll),然后再保存子 (Choices)。

代码如下:

首先,我得到的所有测试用例的错误:

Failure/Error: @poll = FactoryGirl.build(:poll_with_choices, user: @user)
 ActiveRecord::RecordInvalid:
   Validation failed: Choices choices required at least 2

投票模型:

class Poll < ActiveRecord::Base
  belongs_to :user
  has_many :choices
  accepts_nested_attributes_for :choices

  validates :title, presence: true
  validates_each :choices do |record, attr, value| 
    record.errors.add attr, "choices required at least 2" if record.choices.length < 2
  end
end

投票工厂:

FactoryGirl.define do
  factory :poll do
    title { FFaker::Lorem.phrase }
    description { FFaker::Lorem.sentences }
    user

    factory :poll_with_choices do  
      transient do 
        choices_count 3
      end

      after(:build) do |poll, evaluator|
        build_list(:choice, evaluator.choices_count)
      end
    end
  end
end

选择工厂:

FactoryGirl.define do
  factory :choice do
    label { FFaker::Name.name }
    votes 0
    poll
  end
end

投票规范

require 'rails_helper'

RSpec.describe Poll, type: :model do
  before do 
    @user = FactoryGirl.create(:user)
    @poll = FactoryGirl.build(:poll_with_choices, user: @user) 
  end

  subject { @poll }

  it { should respond_to(:title) }
  it { should respond_to(:description) }

  it { should validate_presence_of(:title) }

  it { should belong_to(:user) }
  it { should have_many(:choices) }
  it { should accept_nested_attributes_for(:choices) }

  describe "#save" do 
    before do 
      @user = FactoryGirl.create(:user)
    end

    it "success" do 
      poll = FactoryGirl.build(:poll_with_choices, user: @user)
      expect(poll.save).to eql true
    end

    it "fail" do 
      poll = FactoryGirl.build(:poll, user: @user)
      poll.choices = FactoryGirl.build_list(:choice, 1)
      expect(poll.save).to eql false
    end
  end
end

作为FactoryGirl.create的参考,不是 FactoryGirl.buildhttp://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md#Associations

谢谢,提前。

【问题讨论】:

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


    【解决方案1】:

    最后我可以使用 FactoryGirl trait 和 attributes

    这是poll工厂代码:

    FactoryGirl.define do
      factory :poll do
        title { FFaker::Lorem.phrase }
        description { FFaker::Lorem.sentences }
        user
        choices_attributes { [FactoryGirl.attributes_for(:choice),     FactoryGirl.attributes_for(:choice), FactoryGirl.attributes_for(:choice)] }
    
        trait :with_many_choices do 
          choices_attributes { [
            FactoryGirl.attributes_for(:choice), FactoryGirl.attributes_for(:choice), 
            FactoryGirl.attributes_for(:choice), FactoryGirl.attributes_for(:choice), 
            FactoryGirl.attributes_for(:choice), FactoryGirl.attributes_for(:choice)
          ] }
        end
    
        trait :with_invalid_choices do 
          choices_attributes { [FactoryGirl.attributes_for(:choice),     FactoryGirl.attributes_for(:choice), FactoryGirl.attributes_for(:choice, label: '')] }
        end
    
        trait :with_lack_of_choices do 
          choices_attributes { [FactoryGirl.attributes_for(:choice)] }
        end
    
        trait :without_choices do 
          choices_attributes { [] }
        end
      end
    end
    

    参考资料(感谢):

    【讨论】:

      【解决方案2】:

      如果我没看错,你的工厂有错误:

      FactoryGirl.define do
        factory :poll do
          title { FFaker::Lorem.phrase }
          description { FFaker::Lorem.sentences }
          user
      
          factory :poll_with_choices do  
            transient do 
              choices_count 3
            end
      
            after(:build) do |poll, evaluator|
              build_list(:choice, evaluator.choices_count, poll: poll)
            end
          end
        end
      end
      

      您需要将投票分配给选项,否则选项工厂将为每个选项创建一个投票。

      【讨论】:

      • 好的,我在您的代码中看到了问题。需要存在一个轮询对象,以便可以添加选择(您需要 id 来在选择中引用它) - 但是如果您在父对象存在之前验证依赖对象(选择)的存在,您正在调用问题并且需要像你一样使用奇怪的代码。您应该重新考虑验证策略。
      【解决方案3】:

      这就是我在我的案例中所做的:

      FactoryBot.define do
        factory :store do
          sequence(:name) { |n| "Store #{n}" }
          association :user
          sellers_attributes [ FactoryBot.attributes_for(:seller) ]
        end
      end
      

      我不得不重构我的测试并重命名一些东西。

      【讨论】:

        猜你喜欢
        • 2015-05-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-14
        • 1970-01-01
        • 1970-01-01
        • 2014-01-12
        • 1970-01-01
        相关资源
        最近更新 更多