我正在使用Rails 2.2项目来更新它.我正在用工厂(使用factory_girl)替换现有的固定装置,但出现了一些问题.问题在于用查找数据表示表的模型.当我创建一个购物车,其中有两个产品具有相同的产品类型时,每个创建的产品都在重新创建相同的产品类型.此错误源于ProductType模型上的唯一验证.

问题演示

这是一个单元测试,我创建了一个购物车,并将其组装成若干块.我不得不这样做来回避这个问题.不过,这仍然说明了问题所在.我会解释的.

cart = Factory(:cart)
cart.cart_items = [Factory(:cart_item, 
                           :cart => cart, 
                           :product => Factory(:added_users_product)),
                   Factory(:cart_item, 
                           :cart => cart, 
                           :product => Factory(:added_profiles_product))]

添加的两个产品类型相同,创建每个产品时,都会重新创建产品类型并创建副本.

生成的错误是:

变通方法

本例的解决方法是覆盖正在使用的产品类型,并传入特定实例,以便只使用一个实例."add_product_type"会提前获取并传递给每个购物车项目.

cart = Factory(:cart)
prod_type = Factory(:add_product_type)   #New
cart.cart_items = [Factory(:cart_item,
                           :cart => cart,
                           :product => Factory(:added_users_product,
                                               :product_type => prod_type)), #New
                   Factory(:cart_item,
                           :cart => cart,
                           :product => Factory(:added_profiles_product,
                                               :product_type => prod_type))] #New

问题

使用工厂女孩与"挑选名单"类型关联的最佳方式是什么?

我希望工厂定义包含所有内容,而不是在测试中组装,尽管我可以接受它.

背景和额外细节

工厂/产品.rb

# Declare ProductTypes

Factory.define :product_type do |t|
  t.name "None"
  t.code "none"
end

Factory.define :sub_product_type, :parent => :product_type do |t|
  t.name "Subscription"
  t.code "sub"
end

Factory.define :add_product_type, :parent => :product_type do |t|
  t.name "Additions"
  t.code "add"
end

# Declare Products

Factory.define :product do |p|
  p.association :product_type, :factory => :add_product_type
  #...
end

Factory.define :added_profiles_product, :parent => :product do |p|
  p.association :product_type, :factory => :add_product_type
  #...
end

Factory.define :added_users_product, :parent => :product do |p|
  p.association :product_type, :factory => :add_product_type
  #...
end

ProductType的"代码"的目的是让应用程序能够赋予它们特殊的含义.ProductType模型如下所示:

class ProductType < ActiveRecord::Base
  has_many :products

  validates_presence_of :name, :code
  validates_uniqueness_of :name, :code
  #...
end

工厂/购物车.rb

# Define Cart Items

Factory.define :cart_item do |i|
  i.association :cart
  i.association :product, :factory => :test_product
  i.quantity 1
end

Factory.define :cart_item_sub, :parent => :cart_item do |i|
  i.association :product, :factory => :year_sub_product
end

Factory.define :cart_item_add_profiles, :parent => :cart_item do |i|
  i.association :product, :factory => :add_profiles_product
end

# Define Carts

# Define a basic cart class. No cart_items as it creates dups with lookup types.
Factory.define :cart do |c|
  c.association :account, :factory => :trial_account
end

Factory.define :cart_with_two_different_items, :parent => :cart do |o|
  o.after_build do |cart|
    cart.cart_items = [Factory(:cart_item, 
                               :cart => cart, 
                               :product => Factory(:year_sub_product)),
                       Factory(:cart_item, 
                               :cart => cart, 
                               :product => Factory(:added_profiles_product))]
  end
end

当我试图用相同产品类型的两个项目定义购物车时,我得到了上述相同的错误.

Factory.define :cart_with_two_add_items, :parent => :cart do |o|
  o.after_build do |cart|
    cart.cart_items = [Factory(:cart_item,
                               :cart => cart,
                               :product => Factory(:added_users_product)),
                       Factory(:cart_item,
                               :cart => cart,
                               :product => Factory(:added_profiles_product))]
  end
end

推荐答案

我遇到了同样的问题,并在我的工厂文件顶部添加了一个lambda,该文件实现了一个单例模式,如果自上一轮测试/规范以来数据库已被清除,该模式也会重新生成模型:

saved_single_instances = {}
#Find or create the model instance
single_instances = lambda do |factory_key|
  begin
    saved_single_instances[factory_key].reload
  rescue NoMethodError, ActiveRecord::RecordNotFound  
    #was never created (is nil) or was cleared from db
    saved_single_instances[factory_key] = Factory.create(factory_key)  #recreate
  end

  return saved_single_instances[factory_key]
end

然后,使用示例工厂,可以使用工厂属性来运行lambda

Factory.define :product do |p|
  p.product_type  { single_instances[:add_product_type] }
  #...this block edited as per comment below
end

瞧!

Ruby-on-rails相关问答推荐

如何在_serialize之前执行代码?""或者如何在序列化属性之前对属性进行清理?

仅当未在带有ENV[';Dyno';]的Heroku问题上运行时才初始化Sidekiq Cron

Rails 7.1中的覆盖脚手架控制器

如何修改使用Google Cloud App Engine部署rails 7应用程序的步骤

查询Where Date.parse Rails

params.permit ruby​​3.2.1 更新后 Active::Record 的未定义方法=~

你如何发布到 Capybara 中的 URL?

Rails - 如何从 http://example.com 重定向到 https://www.example.com

Ruby Sinatra Web 服务在 localhost:4567 上运行,但不在 IP 上

rails select标签,预先 Select 了多个值

跳过模型中的某些验证方法

价格字段的字符串、小数或浮点数据类型?

使用 Devise 令牌登录,这是内置的吗?

activerecord 查找所有未包含在数组中的内容

在 Rails 应用程序中处理大文件上传的最佳方法是什么?

Rails 3.1 Sprockets 需要指令 - 有没有办法排除特定文件?

测试和规格有什么区别?

Ruby 中的类别名

在 Rails 3 中突出显示当前页面的最佳方法? -- 有条件地将css类应用于链接

在 Ruby on Rails 中设置日志(log)记录级别