具有深厚关联链的FactoryGirl的最佳做法?

Jar*_*son 5 ruby bdd rspec ruby-on-rails factory-bot

我正在Rails中建模一个复杂的采购工作流程,该流程将请购单转换为订单。我正在使用FactoryGirl进行测试,一切都很好,直到尝试测试OrderLineItem,该OrderLineItem依赖于Order和Quote,后者分别依赖于其他对象,依此类推...

有问题的测试检查受产品影响的OrderLineItem的行为,该行为是产品链上的多个关联。

有没有设置FactoryGirl的好方法,这样我就可以轻松构建OrderLineItems并指定链中较高对象的行为,而不必一次考虑每个对象?

这是我的对象图:

class Requisition
  has_many :requisition_line_items
  has_many :orders
end

class RequisitionLineItem
  belongs_to :requisition
  belongs_to :product
  has_many :quotes
end

class Quote
  belongs_to :line_item
  belongs_to :vendor
  has_one :order_line_item
end

class Order
  belongs_to :requisition
  belongs_to :vendor
  has_many :order_line_items
end

class OrderLineItem
  belongs_to :order
  belongs_to :quote
  has_many :assets
end

class Asset
  belongs_to :order_line_item
  belongs_to :product
end

class Product
  has_many :assets
end

class Vendor
  has_many :orders
end
Run Code Online (Sandbox Code Playgroud)

看似复杂的模型允许根据供应商的报价将购买的“建议”转换为一个或多个实际订单,并且当物品到达时,会为其赋予资产标签。然后可以将资产本身链接回订单和供应商,以在以后提供支持。

这是我的OrderLineItem规范,我有一个比较简洁的设置:

describe '#requires_tag?' do

  let(:product)              { FactoryGirl.create :product, requires_tag: false }
  let(:purchase_requisition) { FactoryGirl.create :purchase_requisition }
  let(:line_item)            { FactoryGirl.create :line_item, 
                                 purchase_requisition: purchase_requisition, 
                                 product: product }
  let(:quote)                { FactoryGirl.create :quote, 
                                 line_item: line_item, unit_price: 0 }

  subject { FactoryGirl.build :order_line_item, quote: quote }

  context 'when neither product nor price require a tag' do
    its(:requires_tag?) { should be_false }
  end

  context 'when product requires a tag' do
    let(:product) { FactoryGirl.create :product, requires_tag: true }
    its(:requires_tag?) { should be_true }
  end

end
Run Code Online (Sandbox Code Playgroud)

我是否真的需要无数的let语句,还是有更好的方法来构建OrderLineItem并对其所依赖的Product属性进行控制?

Jef*_*f D 4

我不得不不同意cpuguy。我同意德米特定律是一件伟大的事情,但由于关系数据库和所存储的分层数据之间的阻抗不匹配,您的对象图似乎只是违反了该定律。

如果这里有一些东西可以重构,它可能是你的模型结构,或者你的模型存储机制。您遇到的 Demeter 问题是您使用关系系统对分层数据模型进行建模这一事实的症状。考虑所有订单信息是否都在一个大哈希中。我认为你不会感受到同样程度的痛苦。唯一的替代方法是尝试将其中一些字段复制到您正在使用它们的位置。

我实际上认为你的规范非常好,因为:a)它们是行为性的,测试听起来像是不太可能改变的业务功能的离散元素b)如果你的规范需要模拟内部结构,那么规范在以下方面变得毫无用处:重构,因为你的期望必须随着它们而改变。

主要问题在于如何构建测试环境。您可以将它们抽象到更高级别的工厂中,但要小心,不要最终隐藏您的规范的独特之处。不过,你在这方面也做得很好。我可能建议的一个建议是在每个上下文中创建一个 let(:requires_tag) ,一个为 true,一个为 false。然后将其他所有内容保留在您的设置中。这样就可以清楚地了解每个上下文与主要设置的差异,这可能需要更长的时间才能理解。

除此之外,如果有更好的方法来做到这一点,我还没有找到。