我为我的模特民意调查编写测试。检查,使用嵌套的属性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
答案 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