Best practice for FactoryGirl with deep association chains?

I am modeling a complex buying process in Rails that converts Requisitions into Orders. I use FactoryGirl to do my testing, and everything is fine until I try to check the OrderLineItem, which depends on the order and quote, each of which depends on other objects, etc ....

The test being tested checks the behavior on the OrderLineItem affected by the Product, which is a few associations above the chain.

Is there a good way to customize FactoryGirl so that I can easily create OrderLineItems and also dictate the behavior of the objects above in the chain without factoring each object one at a time?

Here is my graph object:

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 

Apparently, the complex model allows you to convert a purchase β€œoffer” into one or more actual orders based on quotes from suppliers, and when the goods arrive, they are assigned asset tags. Then the assets themselves can be tied to the order and the supplier for support later.

This is my OrderLineItem specification, I have a rather complicated setup:

 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 

Do I really need myriad let instructions, or is there a better way to create an OrderLineItem and establish control over the attributes of the Product on which it depends?

+4
source share
2 answers

I must disagree with w / cpuguy. I agree that the demeter law is great, but your object graph seems to violate it because of an impedance mismatch between your relational database and stored hierarchical data.

If there is something that could be reorganized, it could be your model structure or model storage mechanism. The Demeter problem you are experiencing is a symptom of the fact that you are using a relational system to model a hierarchical data model. Think about whether all the order information was in a large hash only. I do not think that you will feel the same level of pain. The only alternative to this is to try to copy some of these fields to where you use them.

I really think your specifications are great, because: a) They are behavioral, checking what sounds like separate elements of business functionality that are unlikely to change b) If your specifications require bullying inside, the specifications become useless in terms of refactoring in the future , because your expectations should change with their help.

The main problem is how to create test environments. You can abstract them to a higher factory level, but be careful not to hide what makes your specification unique. You did a good job of this too. One recommendation I could suggest would be to create let (: require_tag) in each context, one with it is true and one with it is false. Then leave everything else in the settings. Thus, it turns out how each context changes from the main setting, which may take a little longer.

Also, if there is a better way to do this, I have not found it.

+3
source

I would say try reorganizing this so that you test one. If you do this, you do not need to create all of these objects (and, in turn, slow down your test).

If you do not break LoD, https://www.google.com/search?q=law+of+demeter&ie=UTF-8&oe=UTF-8&hl=en&client=safari#itp=open0 , it becomes much easier.

You should be able to stub the methods that you call for related objects as needed, instead of creating the actual relationships.

+1
source

Source: https://habr.com/ru/post/1480188/


All Articles