创建对象 FactoryGirl 时未定义的方法。
Undefined method when creating an object FactoryGirl.
我为我的模型投票编写测试。检查,创建具有嵌套属性的对象轮询 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
I 运行 poll_spec.rb 并给出错误:
1) 投票
Failure/Error: @poll = FactoryGirl.create(:poll_with_vote1)
没有方法错误:
#
的未定义方法 `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
您收到此错误的原因是您与这样的名称没有关联。尝试:
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
这样的工厂 - 它们无助于理解正在发生的事情,并且您正在失去特征的力量。就像这样使用它:create :poll, :with_vote_option_1
。它还将允许您通过 create :poll, :with_vote_option_1, :with_vote_option_2
同时使用这两个特征(暂时忽略这些特征的问题,请参阅下一点)
Traits是为了让你的工厂更加清晰。很好的例子可能是枚举字段的特征 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)
少得多 self-explanatory。你不能对随机工厂做第一选择。此外,当您将页面更改为具有标题 '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
我为我的模型投票编写测试。检查,创建具有嵌套属性的对象轮询 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
I 运行 poll_spec.rb 并给出错误: 1) 投票 Failure/Error: @poll = FactoryGirl.create(:poll_with_vote1) 没有方法错误: #
的未定义方法 `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
您收到此错误的原因是您与这样的名称没有关联。尝试:
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
这样的工厂 - 它们无助于理解正在发生的事情,并且您正在失去特征的力量。就像这样使用它:create :poll, :with_vote_option_1
。它还将允许您通过create :poll, :with_vote_option_1, :with_vote_option_2
同时使用这两个特征(暂时忽略这些特征的问题,请参阅下一点)Traits是为了让你的工厂更加清晰。很好的例子可能是枚举字段的特征
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)
少得多 self-explanatory。你不能对随机工厂做第一选择。此外,当您将页面更改为具有标题 '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