Ошибка в пользовательской проверке уникальности для rspec - PullRequest
0 голосов
/ 01 июня 2018

Я пытаюсь создать тест rspec для пользовательской проверки расширения spree (например, драгоценного камня). Мне нужно проверить уникальность variants option values для product (все Spree модели). Вотбазовая структура моделей (хотя они являются частью spree, здания электронной коммерции на основе рельсов):

class Product
  has_many :variants
  has_many :option_values, through: :variants #defined in the spree extension, not in actual spree core
  has_many :product_option_types
  has_many :option_types, through: :product_option_types
end


class Variant
  belongs_to :product, touch: true
  has_many :option_values_variants
  has_many :option_values, through: option_values
end

class OptionType
  has_many :option_values
  has_many :product_option_types
  has_many :products, through: :product_option_types
end

class OptionValue
  belongs_to :option_type
  has_many :option_value_variants
  has_many :variants, through: :option_value_variants
end

Поэтому я создал пользовательскую проверку для проверки уникальности значений вариантов вариантов для определенноготовар.Это продукт (скажем, product1) может иметь много вариантов.И вариант со значениями параметров, скажем, (красный (Option_type: Color) и Circle (Option_type: Shape)) должен быть уникальным для этого продукта

В любом случае, это пользовательский валидатор

 validate :uniqueness_of_option_values

 def uniqueness_of_option_values
 #The problem is in product.variants, When I use it the product.variants collection is returning be empty. And I don't get why.
   product.variants.each do |v|
   #This part inside the each block doesn't matter though for here.
     variant_option_values = v.option_values.ids
     this_option_values = option_values.collect(&:id)
     matches_with_another_variant = (variant_option_values.length == this_option_values.length) && (variant_option_values - this_option_values).empty?
     if  !option_values.empty? &&  !(persisted? && v.id == id) && matches_with_another_variant
       errors.add(:base, :already_created)
     end
   end
 end

И, наконец, вот спецификации

require 'spec_helper'

describe Spree::Variant do

  let(:product) { FactoryBot.create(:product) }
  let(:variant1) { FactoryBot.create(:variant, product: product) }

  describe "#option_values" do
    context "on create" do
      before do
        @variant2 = FactoryBot.create(:variant, product: product, option_values: variant1.option_values)
      end

      it "should validate that option values are unique for every variant" do
      #This is the main test. This should return false according to my uniqueness validation. But its not since in the custom uniqueness validation method product.variants returns empty and hence its not going inside the each block.
        puts @variant2.valid?


        expect(true).to be true #just so that the test will pass. Not actually what I want to put here
      end
    end
  end
end

Кто-нибудь знает, что здесь не так.Заранее спасибо

1 Ответ

0 голосов
/ 02 июня 2018

Я догадываюсь, что происходит.Я думаю, что исправлением было бы изменить вашу проверку следующей строкой:

product.variants.reload.each do |v|

Я думаю, что случается, что когда вы вызываете variant1 в своем тесте, он запускает проверку для варианта1, которыйзвонит variants на объекте продукта.Это запрашивает базу данных для связанных вариантов, и получает пустой результат.Однако, так как вариант 2 имеет тот же фактический объект продукта, этот объект продукта не будет повторно запрашивать базу данных и помнит (неправильно), что его варианты являются пустым результатом.

Еще одно изменение, которое может привести к выполнению теста:изменить свой тест следующим образом:

before do
  @variant2 = FactoryBot.create(:variant, product_id: product.id, option_values: variant1.option_values)
end

Это тонкий, и я хотел бы знать, работает ли он.Это устанавливает поле product_id для варианта 2, но не устанавливает объект product, чтобы ассоциация была фактическим тем же объектом product, который имеет вариант1.(На практике это более вероятно происходит в вашем реальном коде, потому что объект продукта не используется совместно с вариантами объектов.)

Еще одна вещь для вашего правильного решения (если все это правильно) - перезагрузитьно поместите весь свой код сохранения (и ваш код обновления) в транзакцию.Таким образом, не будет условия гонки двух вариантов, которые будут конфликтовать, потому что в транзакции первый должен завершить проверку и сохранить до того, как второй выполнит свою проверку, поэтому он обязательно обнаружит другой, который только что сохранил.

Некоторые предлагаемые методы отладки:

  • Если возможно, просмотрите журнал, чтобы увидеть, когда выполняются запросы.Возможно, вы поймали, что вторая проверка не запрашивала варианты.
  • Проверьте object_id.Возможно, вы поймали, что объекты продукта на самом деле были одним и тем же объектом.
  • Также проверьте new_record?, чтобы убедиться, что вариант1 сохранен перед проверкой варианта2.Я думаю, что это спасает, но было бы приятно узнать, что вы это проверили.
...