Rspec, Factory Girl构建has_many关系

3

我有一个具有一对多关系的模型,它们是PollChoice

如何正确测试它们,因为以下代码会导致ActiveRecord::RecordInvalid:错误。我想要在创建父对象(Poll)时同时创建其子对象(Choice),而不是先创建父对象(Poll),然后再保存子对象(Choice)。

以下是代码:

首先,这是我在所有测试用例中遇到的错误:

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.build:http://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md#Associations。提前感谢。
3个回答

4

最终我使用了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

参考文献(感谢):


注意:本文内涉及IT技术相关内容。

0

以下是我在我的情况下所做的:

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

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


0

如果我看得没错的话,你的工厂里有一个错误:

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来引用选项)-但是如果在父对象存在之前验证依赖对象(选项)的存在,则会出现问题,并且需要采用像您所拥有的奇怪代码一样的方法。您应该重新考虑验证策略。 - BooVeMan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接