我正在尝试在狂欢扩展中创建自定义验证的rspec测试(如gem)
我需要验证variants
的唯一性
option values
{所有product
型号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:颜色)和圆形(Option_type:形状))对于此产品必须是唯一的
无论如何,这是自定义验证器
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
有人知道这里有什么不对。提前致谢
答案 0 :(得分: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。我认为它确实可以保存,但知道你检查过它会很高兴。