sRails 4.2 / Rspec / rspec-retry - ассоциация принадлежит / has_many не удалось - PullRequest
0 голосов
/ 26 июня 2018

На 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. Надеюсь, что это будет более последовательным и эффективным.

...