Ruby on rails rspec的自定义唯一性验证出错
我正在尝试创建一个rspec测试,以便在像gem这样的spree扩展中进行自定义验证 我需要验证变量的唯一性 产品所有Spree型号的选项值 以下是Model Salt的基本结构,尽管它们是spree的一部分,spree是一个基于rails的电子商务建筑:Ruby on rails rspec的自定义唯一性验证出错,ruby-on-rails,rspec,spree,Ruby On Rails,Rspec,Spree,我正在尝试创建一个rspec测试,以便在像gem这样的spree扩展中进行自定义验证 我需要验证变量的唯一性 产品所有Spree型号的选项值 以下是Model Salt的基本结构,尽管它们是spree的一部分,spree是一个基于rails的电子商务建筑: class Product has_many :variants has_many :option_values, through: :variants #defined in the spree extension, not in
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,可以有很多变体。还有一个带有选项值的变体,可以说RedOption\u type:Color和CircleOption\u 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
有人知道这里出了什么问题。提前谢谢我猜发生了什么事。我认为解决方法是使用以下行更改验证:
product.variants.reload.each do |v|
我认为发生的是,当您在测试中调用variant1时,它正在运行variant1的验证,该验证调用产品对象上的变量。这将查询数据库中的相关变量,并得到一个空结果。但是,由于variant2具有相同的实际产品对象,因此该产品对象不会重新查询数据库,并且错误地记住其变体是空结果
另一个可能使测试运行的更改是按如下方式更改测试:
before do
@variant2 = FactoryBot.create(:variant, product_id: product.id, option_values: variant1.option_values)
end
这是微妙的,我想知道它是否有效。这将在variant2上设置product_id字段,但不会将关联的product对象设置为variant1具有的实际相同的product对象。实际上,这更可能发生在您的实际代码中,即产品对象不在变量对象之间共享
如果所有这些都正确,正确解决方案的另一件事是重新加载,但将所有保存代码和更新代码放在事务中。这样就不会有两个变量的竞争条件发生冲突,因为在一个事务中,第一个变量必须在第二个变量进行验证之前完成验证和保存,所以它一定会检测到刚刚保存的另一个变量
一些建议的调试技术:
如果可能,请查看日志以查看何时进行查询。您可能发现第二次验证没有查询变体。
检查对象id。您可能发现产品对象实际上是同一个对象。
还要检查新的记录吗?确保在测试variant2之前保存variant1。我认为它确实节省了,但如果知道你检查了它,那就太好了。
我马上注意到您正在通过let设置variant1,并设置@variant2,然后调用@variant.valid?但是@variant从未设置过。@MarlinPierce。。哦,我在这里写错了。我的意思是@variant2 only.OMG有效。。非常感谢你。将其更改为produc_id:product.id有效。所以你提到了比赛条件。这就是这里发生的事情吗?我不知道怎么做。你在测试独特性。如果另一条记录尚未在数据库中,但同时正在验证,该怎么办?下一个变体尚未保存,因此没有一个检测到非唯一性,两个都保存。