我在rspec上面临一个问题的庞然大物,每次我尝试解决一个错误时,它都会创建另一个错误。
我有一个具有多个步骤的交易模型
model / step.rb
belongs_to :deal, :foreign_key => 'deal_id'
model / deal.rb
has_many :steps, dependent: :destroy do
# added to enable maximum nb of steps a deal can have
# to take into account in active admin
# in order to calculate the correct new nb of step to compare to the authorized limit
# source: homeonrails.com/2012/10/validating-nested-associations-in-rails/
def length
reject(&:marked_for_destruction?).length
end
end
我所有错误的要点是如何使我的Feature rspec测试工作之一,在那里我将Deal和Steps关联起来。我以前使用的是Factory Girl“常规”瞬态,它会更干净,但由于我们有特殊要求,因此我不得不放弃它(请参阅下文)。
factory :deal_with_associated_steps do
to_create {|instance| instance.save(validate: false) } # skip validate
transient do
steps_count 27
end
after(:create) do |deal, evaluator|
create_list(:steps, evaluator.steps_count, deal: deal)
end
end
我放弃使用这种“瞬态”技术来创建与交易相关的多个步骤的原因是我们的应用程序非常特定的。
在进行给定交易时,所有关联步骤的st_appearance_order_nb(整数)必须始终为: -从0开始 -然后没有空隙,所以1,2,3 ...
一些关于Deal模型的before_validations交易使我能够确保始终是这种情况。您不能拥有与相关步骤相关的交易,其中一个步骤的外观_nb为1,另一个步骤的appearance_nb为3,但是没有一个外观_nb为2的步骤,并且您的步骤也不能为外观_nb为2的步骤。 0。它必须是系列0、1、2、3 ...以此类推
这实际上仍然可以使用“经典瞬态”方式在Factory Girl中创建“步骤”。但是上面创建步骤的经典“瞬态”方式的问题在于,我有一个gem called "rspec-retry"可以像许多其他具有复杂UI / javascript页面的rspec用户一样,帮助我重新进行功能测试,有时最终测试由于某种js / loading原因而第一次失败,然后如果您重复了足够多次,则第2或第3次它将起作用。因此,我的大多数功能测试都运行了4次,有的仅通过了第二次或第三次:) gem rspec-retry很简洁,但是与我在Factory Girl中创建与之关联的步骤的“瞬态”方式有一个非常重要的问题:
我遇到了错误,因为如果测试第二次“一次重试”一次失败,就好像测试应用程序认为st_appearance_order_nb nb 0至4已经被使用(实际上,它们是由第一个rspec“ try”创建的) ),因此它现在创建4个新步骤,其st_appearance_order_nb分别为5、6、7和8。
然后...导致错误,因为我有一个before_validation来确保与交易相关的步骤的st_appearance_order_nb始终从0开始,然后逐个递增
因此,使用rspec-retry,我无法使用Factory Girl临时方法来创建关联的步骤,至少那是我在发现另一种方法时得出的结论:我决定“手动以此方式创建关联的步骤
let!(:deal_with_videos) { create(:deal,
title: "title deal 1" ) }
video_urls = [ "", # no video allowed on first step
"https://www.facebook.com/418234631906607/videos/495053617558041", # square video (5 sec)
"https://www.facebook.com/pili.morillo.56/videos/352355988613922", # landscape video with internal black sidebars
"https://www.facebook.com/rihanna/videos/10155330511896676/", # landscape video no internal black sidebars
"https://www.facebook.com/videos/1592424637461205/", # portrait video
""
]
(0..5).each do |n|
let!(:"deal_with_videos_step#{n}") {
FactoryGirl.create(:step,
st_appearance_order_nb: n,
st_video_url: video_urls[n],
deal: deal_with_videos)
}
end
这可以修复错误,并且我的99%的测试都可以正常工作,但现在的问题是:我的一个测试失败了:因为很奇怪,我与Deal和Steps关联的方式没有完全完全工作,但只有一部分...让我补充说,在生产和开发模式下一切正常。
describe "on Deal Page load, the view behaves appropriately in terms of video" do
let(:action) { visit actual_deal_page_path(deal_with_videos) }
let(:fb_player_visibility) { "hidden" }
let(:video_href_set_by_app_js) { nil.to_s }
it_behaves_like "a view where the FB video player behaves appropriately"To be clear I found a very hack way to do stuff, but it created another issue in a ripple effect a new bug as the way I was doing it was making the test suite think there was no new
由于我现在知道的原因而导致测试失败:测试失败,因为以下before_validation的内容从未执行
models/deal.rb
before_validation :extract_st_embed_hostings_from_st_video_urls
def extract_st_embed_hostings_from_st_video_urls
puts "beacon1"
self.steps.reject(&:marked_for_destruction?).each do |step|
puts "beacon2"
# do stuff
end
end
我知道,因为这是带有那些puts消息的测试环境中的问题,当我在测试块上运行rspec test时,我只会看到“ beacon1”,而没有看到beacon2(在dev和prod中都看到这两个消息)< / p>
我想知道为什么它没有被执行。
因此,我在测试中添加了一些puts
来查看为什么行self.steps.reject(&:marked_for_destruction?).each do |step|
没有输出任何内容。我的交易和步伐关联在测试中无法正常工作吗?
describe "on Deal Page load, the iew behaves appropriately in terms of video" do
before do
action
puts deal_with_videos.steps.to_json
puts deal_with_videos.steps[1].to_json
puts deal_with_videos.id
puts deal_with_videos_step0.deal_id
puts deal_with_videos_step0.deal.title
puts deal_with_videos_step0.to_json
end
let(:action) { visit actual_deal_page_path(deal_with_videos) }
let(:fb_player_visibility) { "hidden" }
it_behaves_like "a view where the FB video player behaves appropriately"
end
结果很奇怪:
“把deal_with_videos.steps.to_json放进去”给了我[] =>看来他们没有关联
“将deal_with_videos.steps 1。to_json放入” null”,使其与先前的puts
但是netx 2 put带来了更多混乱:
“放入deal_with_videos.id”给出3
“ put deal_with_videos_step0.deal_id”也给了我3
所以从两个方向来看,我有相同的信息,这很奇怪:看来它们实际上联系得很好。奇怪的是,这与我的前2个推杆相矛盾。
放入deal_with_videos_step0.deal.title赋予sme“ title deal1”
放置deal_with_videos_step0.to_json给了我一个详细的json,其中包含内容(此处未复制以保持简洁) =>它们都起作用
我的结论
这就像我联系他们的方式只有一种方式: 如果我先执行诸如deal_with_videos_step0之类的步骤,然后使用.deal进行操作以到达Deal表,那么它就可以工作。
但是反过来说,在我的before_validation中名为extract_st_embed_hostings_from_st_video_urls(见上文)的那个无法正常工作,则无法正常工作:如果我在表上开始Deal,然后请求所有与交易,它不起作用,它给了我空的输出。因此,下面的请求为空,这就是为什么验证之前extract_st_embed_hostings_from_st_video_urls不执行任何操作的原因,测试套件认为没有步骤可以进行操作。
所以我在这里被屏蔽:我的问题是在工厂女孩+ rspec-retry +我特定的交易模型的相关步骤属性约束的十字路口
在使用rspec-retry的同时,如何在我的测试中关联一个交易和多个步骤,并设法使此测试通过,也就是说,要设法使self.steps.reject(&:marked_for_destruction?)。each即使在测试环境中也可以“工作”,而不是认为没有“步骤”相关联?
编辑
在下面的评论中提供更多信息
1 / st_appearance_order_nb
st_appearance_order_nb仅仅是“步骤”的一个属性/列。通过has_many关系直接在交易表单内的 中添加了Ut:
f.inputs "Steps" do
f.has_many :steps,
allow_destroy: true,
heading: false,
new_record: true,
# ensure each new step is automagically assigned a +1st_appearance_order_nb
sortable: :st_appearance_order_nb,
sortable_start: 0 do |step|
step.input :st_appearance_order_nb,
input_html: { readonly: true, disabled: true },
label: "Appearance rank"
step.input :st_video_url,
end
end
models / deal.rb
before_validation :assign_new_st_appearance_order_nb_values_for_steps_in_case_of_steps_removals
before_validation :check_steps_start_on_zero
before_validation :check_steps_have_no_gap_for_st_appearance_order_nb
# in case one or more Steps are removed, avoid a "hole"
# in the st_appearance_order_nb due to those removals
# includes the other requirement to re-start the ranks at 0
def assign_new_st_appearance_order_nb_values_for_steps_in_case_of_steps_removals
if self.steps.any? && self.steps.select { |st| st.marked_for_destruction? }.any? # restrict this taxing operation to cases where there are removals
remaining_steps = self.steps.reject(&:marked_for_destruction?)
remaining_steps.sort_by(&:st_appearance_order_nb).each_with_index do |step, index|
step.update_attributes st_appearance_order_nb: index
end
end
end
def check_steps_start_on_zero
if self.steps.any?
if self.steps.map{|u| u.st_appearance_order_nb}.min != 0
errors[:base] << "Error on Steps. There must be at least one Step with Appearance rank equal to 0 ."
end
end
end
def check_steps_have_no_gap_for_st_appearance_order_nb
if self.steps.any?
if !array_one_steps_increment?( self.steps.map{|u| u.st_appearance_order_nb} )
errors[:base] << "Error on Steps: you can't have gaps inside the list of Appearance ranks. Only increments by one. Change the Appearance ranks accordindly."
end
end
end
def array_one_steps_increment?(array)
sorted = array.sort
lastNum = sorted[0]
sorted[1, sorted.count].each do |n|
if lastNum + 1 != n
return false
end
lastNum = n
end
true
end
编辑
经过几天的搜索但没有成功,但还是放弃了,但是以一种有意义的方式放弃了:确实有很多困难来自于我在功能规范中对此进行测试的事实,实际上我不应该让应用程序回调“设置”本身”(通过set方法)(有问题的属性)(例如st_embed_hosting),因此我选择仅在功能测试中自己模拟它们,然后进行实际测试以查看回调是否在模型规格中起作用。希望它会更加一致和有效。