我正在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

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

这是我的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

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

最佳答案

我必须不同意cpuguy。我同意demeter的定律是一件好事,但是由于关系数据库和要存储的层次结构数据之间的阻抗不匹配,因此对象图似乎只是违反了它。

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

我实际上认为您的规范很棒,因为:
a)他们的行为举止,测试听起来像是业务功能中离散元素的,不太可能改变的
b)如果您的规范要求模拟内部构造,那么在进行重构时,这些规范将变得毫无用处,因为您的期望必须随它们而改变。

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

除此之外,如果有更好的方法可以做到这一点,我还没有发现。

关于ruby-on-rails - 拥有深厚关联链的FactoryGirl的最佳做法?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16492600/

10-10 15:23