【问题标题】:FactoryGirl circular dependency and validationFactoryGirl 循环依赖和验证
【发布时间】:2014-01-21 10:18:44
【问题描述】:

我是 FactoryGirl 的新手,在 Ruby 中,我正在尝试编写一些 rspec,但在循环依赖方面遇到了一些麻烦。我搜索并尝试了几件事,最后一个生成了无限循环,我不得不关闭我的 WM。 (触发 100%)

模特/投票

class Vote < ActiveRecord::Base
  has_many :vote_options

models/vote_option

class VoteOption < ActiveRecord::Base
  belongs_to :vote
  accepts_nested_attributes_for :vote_options, reject_if: :all_blank

spec/models/vote_spec

describe Vote do
  describe 'should return the good label' do
    let(:nobody) { FactoryGirl.create(:vote, visibility: 0) }
    it "should not be visible" do
      nobody.intercom_custom_data[:visibility].should == I18n.t("votes.new.visibility_none")
    end
  end
end

规格/工厂

factory :vote do
    team
    question 'Question?'
    question_type 0

    # Avoid validation because of the circular dependency.
    # https://stackoverflow.com/questions/9322353/factory-girl-create-that-bypasses-my-model-validation
    after(:build) do |vote|
      vote.vote_options << VoteOption.create(vote_id: vote.id)
      #vote.save!(:validate => false)
    end
  end

  factory :vote_allowed do
    user
    vote
    allowed 1
  end

  factory :vote_casted do
    vote_allowed
    vote_options
  end

  factory :vote_options do
    vote
    title 'default option'
  end

这是执行无限循环的代码。我猜是因为循环依赖,它无限地创建了 vote 和 vote_options。

所以,我得到了一个有很多 vote_options(用户选择)的投票,每个 vote_options 都包含它的父 ID。如果字段为空白,验证会拒绝创建,我们在保存新的 vote/vote_options 的代码方面没有问题,因为一切都在同一个事务中完成。

但是,使用 rspec,我不能同时创建相互依赖的投票和投票选项行。 (或者我不知道该怎么做)

所以,我试图避免这样的验证:

after(:create) do |vote|
  VoteOption.create(vote_id: vote.id)
  vote.save!(:validate => false)
end

但我得到了一个错误:

Failure/Error: let(:nobody) { FactoryGirl.create(:vote, visibility: 0) }
ActiveRecord::RecordInvalid:
   Validation failed: Vote options can't be blank

我之前尝试使用after_create 而不是after(:create),但我的FactoryGirl 是4.3,所以after_create 不再存在。我在https://stackoverflow.com/questions/15003968/undefined-method-after-create-with-factorygirl 找到了一个关于“如何在 >3.3 中使用 after_create”的教程,但它对我不起作用,我不应该得到“投票选项不能为空”......所以我看了一下在上一个链接中链接的文档,我尝试在 spec/factories

中添加
FactoryGirl.define do
  after(:create) {|instance| instance.create }

但我真的不明白我应该做什么,也不明白问题出在哪里。有什么想法吗?

编辑#1:

factory :vote do
    team
    question 'Question?'
    question_type 0

    association :vote_options, factory: :vote_options
    # tried with :create and :build
    after(:create) do |vote, evaluator|
      create_list(:vote_options, 1, vote: vote)
    end
  end

【问题讨论】:

  • 触发此问题的规格是什么样的?
  • 我不确定是否清楚地理解了这个问题。我在其他规格上没有问题。这里的问题是因为模型的设计方式,具有循环依赖关系,并且如果字段为空白,验证拒绝创建新实例,所以我需要避免验证。我用vote.save!(:validate =&gt; false) 完成了它,它似乎有效,因为我没有得到空白字段”但我得到了“after_create is not function”,所以我使用了“after(:create)”但我又得到了“空白字段”,所以我不知道什么在这里不起作用。
  • 你为什么不使用 factorygirl 关联?
  • @majioa 你是什么意思?正如我所说,我是 FactoryGirl/Ruby 的新手,我在一个现有的大型项目上工作,我正在尝试弄清楚很多事情,我对 FactoryGirl 中的关联一无所知。

标签: ruby-on-rails ruby validation rspec


【解决方案1】:

你有没有试过按照document协会一章去做?

ignore do
   vote_options_count = 1
end

after(:build) do | vote, evaluator |
   create_list( :vote_option, evaluator.vote_options_count, vote: vote )
end

之后应该允许访问如下:

create(:vote).vote_options.length # 0
create(:vote, vote_options_count: 15).vote_options.length # 15

注意vote_option 工厂必须定义为单数,而不是复数。

【讨论】:

  • 但是我之前不能定义vote_options,还需要定义vote...^^'
  • @Vadorequest 为什么你不能定义vote_option - 单数,而不是复数?
  • 也许你是对的,所以在 factory.rb 中我将 vote_options 更改为 vote_option 但我得到:Failure/Error: Unable to find matching line from backtrace SystemStackError: stack level too deep
  • @Vadorequest 从简单的工厂votevote_option 开始,没有任何关联或分配。尝试先创建:vote_option,然后:vote,然后添加关联到:vote,然后再次chack。
  • 它会失败,我有DB和模型约束,我需要通过验证。
【解决方案2】:

我确实很喜欢:

规格/工厂

factory :vote_option do
    vote
    title 'default option'
  end

  factory :vote do
    team
    question 'Question?'
    question_type 0
    vote_options_attributes {[{
      title: 'foo'
    }]}
  end

  factory :vote_allowed do
    user
    vote
    allowed 1
  end

  factory :vote_casted do
    vote_allowed
    vote_option
  end

spec/models/vote_spec

describe Vote do

  describe 'should return the good label' do
    let(:nobody) { FactoryGirl.create(:vote, visibility: 0) }

    it "should not be visible" do
      #nobody.intercom_custom_data[:visibility].should == I18n.t("votes.new.visibility_none")
      puts nobody.inspect
      puts nobody.vote_options.inspect
    end

  end
end

控制台

Run options: exclude {:semaphore_ignore=>true}
#<Vote id: 1, users_id: nil, teams_id: 1, datetime: "2014-01-21 13:42:37", question: "Question?", question_type: 0, not_vote: 0, hidden: nil, visibility: 0>
[#<VoteOption id: 1, vote_id: 1, title: "foo", votes: 0, created_at: "2014-01-21 12:42:37", updated_at: "2014-01-21 12:42:37">]

我不使用任何关联,任何列表,我只是在创建投票时强制添加 options_attributes 以自动创建 vote_options。 (就像我们从浏览器创建新投票一样)

谢谢majioa。

【讨论】:

    猜你喜欢
    • 2019-09-17
    • 2020-02-12
    • 2020-11-18
    • 1970-01-01
    • 1970-01-01
    • 2016-02-27
    • 2019-03-26
    • 2021-04-24
    • 1970-01-01
    相关资源
    最近更新 更多