【问题标题】:Error in custom uniqueness validation for rspecrspec 的自定义唯一性验证错误
【发布时间】:2018-06-01 19:28:44
【问题描述】:

我正在尝试为 spree 扩展(如 gem)中的自定义验证创建 rspec 测试 我需要验证 variants 的唯一性 option values 用于product(所有Spree 型号) 这是模型的基本结构(虽然它们是 spree 的一部分,一个基于 Rails 的电子商务建筑):

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)可以有很多变体。并且具有选项值的变体可以说(Red(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

任何人都知道这里出了什么问题。提前致谢

【问题讨论】:

  • 我立即注意到您正在通过 let 设置 variant1,并设置 @variant2 但随后调用 @variant.valid? 但从未设置 @variant
  • @MarlinPierce.. 哦,我在这里写错了。我的意思是 @variant2 而已。

标签: ruby-on-rails rspec spree


【解决方案1】:

我猜测发生了什么。我认为解决方法是使用以下行更改您的验证:

product.variants.reload.each do |v|

我认为正在发生的事情是,当您在测试中调用variant1 时,它正在运行variant1 的验证,它在产品对象上调用variants。这会在数据库中查询相关变体,并得到一个空结果。但是,由于 variant2 具有相同的实际产品对象,该产品对象不会重新查询数据库,并且会(错误地)记住它的变体是一个空结果。

另一个可能使您的测试运行的更改是如下更改您的测试:

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

它很微妙,我想知道它是否有效。这会在 variant2 上设置 product_id 字段,但不会将关联的 product 对象设置为与 variant1 具有的实际相同的 product 对象。 (实际上,这更有可能发生在您的实际代码中,即产品对象不会在变体对象之间共享。)

正确解决方案的另一件事(如果这一切都是正确的)是重新加载,但将所有保存代码(和更新代码)放入事务中。这样就不会有两个变体的竞争条件会发生冲突,因为在事务中,第一个必须在第二个进行验证之前完成验证并保存,所以它一定会检测到另一个刚刚保存的.

一些建议的调试技术:

  • 如果可能,请查看日志以查看何时进行查询。您可能已经发现第二次验证没有查询变体。
  • 检查object_id。您可能已经发现产品对象实际上是同一个对象。
  • 还要检查new_record? 以确保在您测试variant2 之前已保存variant1。我认为它确实可以保存,但很高兴知道您检查过。

【讨论】:

  • 天啊,工作..非常感谢。将其更改为 product_id: product.id 有效。所以你提到了比赛条件。这就是这里发生的事情吗?我不明白怎么做。
  • 您正在测试唯一性。如果另一条记录尚未在数据库中,但同时正在验证中怎么办?下界变体尚未保存,因此没有人检测到非唯一性,并且都保存了。
猜你喜欢
  • 2020-07-01
  • 2016-09-26
  • 1970-01-01
  • 1970-01-01
  • 2017-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多