Factory_bot: Access to constructor attributes hash within dynamic attribute block? + nested association

Created on 27 Nov 2013  Â·  6Comments  Â·  Source: thoughtbot/factory_bot

_I tried asking this twice in the Google Group but both time my post disappeared…:_

I would like to do different things if a certain attribute is passed to the constructor or not (using default), e.g.:

quantity { constructor[:quantity] ? constructor[:quantity] + 5 : 10 }  #lazy attribute

so the quantity will have different value if I specify the quantity or not in the constructor:

FactoryGirl.build :item, quantity: 4   #==> item.quantity == 9
FactoryGirl.build :item   #==> item.quantity == 10

Is this possible?

PS: In the getting started document, I think it would be good to specify that association attributes are processed before other attributes even though they could be defined after them in the define block.

Most helpful comment

I still wish we could access the constructor supplied hash to check what has been supplied to make some choice in the lazy attribute block…

http://stackoverflow.com/questions/31205859/factory-girl-can-we-check-if-an-attribute-has-been-overridden-in-its-lazy-attri

All 6 comments

What you're looking for are transient attributes.

Best of luck!

I'm aware of the transient attributes, however, in my case, I don't want the user of the factory to have to remember to use the transient attribute.
My real case is of course more complicated than this one. Basically, I have a quantity dependency that I need to handle if user set a quantity attribute (by setting the association quantity > quantity).

No 'evaluator' I could tap in the block?

@gamov can you provide the real example? I'm not sure changing quantity like that is actually ideal because it introduces a level of indirection (if you set a value, I'd assume that value be the value assigned and not change).

First of all, thanks for your help. I switched around the requirement and it leads to cleaner factories and only broke a few tests which were easily fixed. Now quantity of the nested association is always valid.

class ShippingItem < ActiveRecord::Base
   belongs_to :purchased_item
   validate { errors.add(:quantity, "cannot be higher than available quantity: #{purchased_item.quantity}") if purchased_item.quantity < quantity }
end

Shipping Item factory:

factory :shipping_item do
    quantity { rand 3..100 }
    purchased_item {association :purchased_item, quantity: (quantity..(quantity + 100)).to_a.sample}
end

This works fine if you set the quantity or not when invoking the shipping item factory (which was my original question).
Let say now that I want to set the delivery site for the purchased item from the shipping item factory. I would do:

factory :shipping_item do
    quantity { rand 3..100 }
    ignore {delivery_site nil}
    purchased_item {association :purchased_item, 
                                quantity: (quantity..(quantity + 100)).to_a.sample,
                                delivery_site: (delivery_site || FactoryGirl.build :delivery_site)
                            }
end

However, I can see it becoming a pattern and doesn't feel 'right' or DRY. Is it the usual way?

@gamov okay, this is starting to make more sense now, and yes, I agree this isn't DRY.

In cases like this (especially because this is likely a simple case and there are MUCH more complicated ones out there), I actually create support classes for building complicated data (either because there's a LOT of associated data or certain aspects change and it impacts creation of other factories, like this). The example I point to in cases like this is here: https://github.com/thoughtbot/factory_girl/issues/230#issuecomment-22414812

At the end of the day, factory_girl can't (and won't!) handle every permutation of every way objects get associated and built. It's just not possible. In cases like yours where there's little things that change, I always recommend solving things with pure Ruby. It's flexible, extensible, and technically testable if you want.

Hopefully this helps - I know "factory_girl can't handle this well" doesn't sound ideal, but it really is, since it keeps factory_girl's interface clean and understandable and removes the chance for odd edge cases.

I still wish we could access the constructor supplied hash to check what has been supplied to make some choice in the lazy attribute block…

http://stackoverflow.com/questions/31205859/factory-girl-can-we-check-if-an-attribute-has-been-overridden-in-its-lazy-attri

Was this page helpful?
0 / 5 - 0 ratings