创建对象FactoryGirl时的未定义方法。

时间:2015-07-02 09:35:04

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

我为我的模特民意调查编写测试。检查,使用嵌套的属性vote_option创建对象轮询。它是我的 Factories.rb:

FactoryGirl.define do

  factory :vote_option do
    title "Ruby"
    poll
  end

  factory :poll do
    topic "What programming language are you using?"

    trait :vote_option1 do
      association :vote_option, title: "C#"
    end

    trait :vote_option2 do
      association :vote_option, title: "Ruby"
    end

    factory :poll_with_vote1, traits: [:vote_option1]
    factory :poll_with_vote2, traits: [:vote_option2]
  end
end   

我创建了测试并检查,该对象已创建。 的 poll_srec.rb

require 'rails_helper'

RSpec.describe Poll, type: :model do
  #let(:user) { FactoryGirl.create(:user) }

  before do
    @poll = FactoryGirl.create(:poll_with_vote1)

  end

  subject{@poll}

  it { should be_valid }

end

我运行poll_spec.rb并给出错误: 1)民意调查      失败/错误:@poll = FactoryGirl.create(:poll_with_vote1)      NoMethodError:        未定义的方法`vote_option ='对于#

为什么会出现这个错误?我的工厂出了什么问题?

Just in case model Poll:
class Poll < ActiveRecord::Base
  VOTE_OPTIONS_MIN_COUNT = 1

  has_many :vote_options, dependent: :destroy
  has_many :votes

  validates :topic, presence: true
  #validates :vote_options, presence: true #association_count: { minimum: VOTE_OPTIONS_MIN_COUNT }


  #validates :user_id, presence: true

  accepts_nested_attributes_for :vote_options, :reject_if => :all_blank,
                                                  :allow_destroy => true

  def normalized_votes_for(option)
    votes_summary == 0 ? 0 : (option.votes.count.to_f / votes_summary) * 100
  end

  def votes_summary
    vote_options.inject(0) { | summary, option | summary + option.votes.count  }
  end
end

1 个答案:

答案 0 :(得分:2)

您收到此错误的原因是您没有与此类名称关联。尝试:

FactoryGirl.define do

  factory :vote_option do
     title "Ruby"
  end

  factory :poll do

    topic "What programming language are you using?"

    trait :vote_option1 do
      after(:create) {|poll| poll.vote_options << create(:vote_option, title: 'C#')         
    end

    trait :vote_option2 do
      after(:create) {|poll| poll.vote_options << create(:vote_option, title: 'Ruby')         
    end

    factory :poll_with_vote1, traits: [:vote_option1]
    factory :poll_with_vote2, traits: [:vote_option2]
  end
end

话虽如此,几个人的做法:

  • 您的测试是代码的文档。任何将来使用该代码的人都应该能够阅读您的测试并理解代码应该做什么。因此 - 您的测试必须具有极高的可读性。

  • 尝试避免创建像poll_with_vote1这样的工厂 - 它们无助于理解什么是hapenning而你正在失去特质的力量。只需使用它:create :poll, :with_vote_option_1。它还允许您使用create :poll, :with_vote_option_1, :with_vote_option_2同时使用这两个特征(暂时忽略这些特征的问题,请参阅下一点)

  • 特点是让您的工厂更加清晰。好的例子可能是enum字段的published包裹status 1逻辑。实际上,你的特征实际上是隐藏了任何阅读你的测试的投票选项的实际硬编码值,这将产生Why the hell is he expecting 'C#' here?。有更好的方法可以做到这一点。

  • 将工厂随机化,而不是依赖硬编码值。它会迫使你写出更清晰的测试。例如,测试expect(page).to have_content('Ruby')expect(page).to have_content(vote_option.title)更不易解释。你不能对随机工厂做第一个选择。当您将页面更改为具有标题“Ruby”时,您的测试将突然变得无用 - 随机数据也不会发生这种情况(嗯,它可以,但它宁可随机传递而错过错误的概率)

  • 您应该始终尝试使您的工厂有效。在您的示例中,工厂poll不是。

这就是我为你的模型编写工厂的方法(使用FFaker gem随机化数据)

FactoryGirl.define do

  factory :vote_option do
     title { FFaker::Lorem.word }
  end

  factory :poll do

    topic { FFaker::Lorem.sentence + '?' }

    transient do    # In old version `ignore do`
      vote_options_number { rand(1..4) }
    end

    after(:build) do |poll, ev|
      poll.vote_options << build_list :vote_option, ev.vote_options_number
    end

end
相关问题