На rspec я столкнулся с проблемой монстра, где каждый раз, когда я пытаюсь устранить ошибку, она создает другую.
У меня есть модель сделки, которая имеет множество шагов
модель / step.rb
belongs_to :deal, :foreign_key => 'deal_id'
модель / 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 работать, где я связываю сделки и шаги . Раньше я использовал «обычный» переходный процесс 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 ...
Некоторые before_validations для модели Deal Deals позволяют мне убедиться, что это всегда так. У вас не может быть сделки со связанными шагами, где один из шагов имеет Appearance_nb, равный 1, а другой шаг имеет Appearance_nb, равный 3, но нет шага с Appearance_nb 2. И вы не можете не шагать с 0. Это просто должна быть серия 0,1,2,3 ... и так далее
На самом деле это все равно будет работать с "классическим переходным" способом создания Steps в Factory Girl. НО проблема с классическим «переходным» способом, описанным выше, для создания шагов заключается в том, что у меня есть гем под названием «rspec-retry» , который помогает мне повторно выполнить тесты функций, как и многие другие пользователи rspec со сложным Страницы пользовательского интерфейса / javascript, иногда мой внешний интерфейс не проходит первый раз по какой-либо причине js / загрузки, а затем, если вы повторите их достаточно, во 2-й или 3-й раз он будет работать. Поэтому большинство моих тестов функций выполняются 4 раза, а некоторые проходят только 2-й или 3-й раз :) 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 and 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
Я знаю, потому что это проблема в тестовой среде с сообщением о путах, когда я запускаю тест rspec на тестовом блоке, я вижу только «beacon1», а не beacon2 (в dev и prod видны оба сообщения)
Я задавался вопросом, почему это не выполняется.
Итак, я добавил внутрь теста немного puts
, чтобы понять, почему строка self.steps.reject(&:marked_for_destruction?).each do |step|
ничего не выводила. Может ли моя ассоциация Deal and Steps не работать в тестах?
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
А результаты странные:
"put deal_with_videos.steps.to_json" дает мне [] => так что кажется, что они не связаны
"ставит deal_with_videos.steps 1 .to_json" дает значение "ноль", соответствующее предыдущему puts
Но сеты netx 2 приносят больше путаницы:
"кладет deal_with_videos.id" дает 3
"ставит deal_with_videos_step0.deal_id" и дайте мне 3
Итак, в двух направлениях у меня есть одна и та же информация, это странно: кажется, что они действительно хорошо связаны. Странно, как это противоречит мне первые 2 пута.
- но тогда последние "путы" дают мне:
помещает deal_with_videos_step0.deal.title и записывать "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 + ограничения атрибутов Steps для моей конкретной модели сделки
Как я могу связать в моих тестах сделку и несколько шагов, используя rspec-retry и управляя, чтобы пройти этот тест, то есть, управляя self.steps.reject (&: mark_for_destruction?). «работать» даже внутри тестовой среды, вместо того, чтобы думать, что «шаг» не связан?
EDIT
Более подробная информация приведена ниже после комментариев
1 / st_appearance_order_nb
st_appearance_order_nb - это только один атрибут / столбец шага. Ut добавлен в Active Admin непосредственно внутри формы сделки через отношение has_many:
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
- Тогда intgerity при добавлении / удалении ...
модель / 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
EDIT
После нескольких дней поисков безуспешно, что-то вроде сдачи, но это имеет смысл: действительно, может быть, так много трудностей возникает из-за того, что я тестирую это в спецификациях Feature, где на самом деле я не должен позволять обратным вызовам приложения "set сам "(через метод set) те атрибуты, которые являются проблемой (например, st_embed_hosting), поэтому я решил просто смоделировать их в тестах Feature и провести реальное тестирование, чтобы увидеть, работают ли обратные вызовы в спецификациях Model. Надеюсь, что это будет более последовательным и эффективным.