Closed douglascamata closed 10 years ago
+1
+1
+1
+1
@douglascamata given how FactoryGirl works, there's no way to tell that bar is an ActiveRecord association; all it's doing is raw assignment by calling foo#bar=
, passing in the built object.
That said, you should be able to use accepts_nested_attributes_for :bar
on the model, and pass bar_attributes: { attribute: value1 }
and have everything work.
@joshuaclayton how come this snippet works if FactoryGirl can't tell if bar
is another factory? I use this syntax in all my factories and FactoryGirl knows these kind of attributes are other factories and creates them for me. And I can tell you right away they are associations factories, 'cause the method calls doesn't have any arguments.
And, as seen in the docs, there's another syntax for associations that is pretty meaningful and can be used to solve issues like this:
factory :post do
# ...
association :author, factory: :user, last_name: "Writely"
end
Totally explicit that author
is an association and would be possible to use that syntax I proposed to override its attributes.
The idea of modify my app's code just to be able to make my tests prettier and smaller is really annoying, IMHO.
@douglascamata from a declaration perspective, yes, declaring the association with arbitrary attributes works correctly. When actually building the object, though (e.g. create :foo, bar: { attr1: 'value' }
), it's not aware of the fact that it's an association.
Some of the code related to this can be viewed here and here.
At the stage of providing overrides, the only thing the evaluator has is the method signatures returning processed values - so for an override, https://github.com/thoughtbot/factory_girl/blob/7e2bed5938be2e1314a7df6a647031a47d01c2c8/lib/factory_girl/evaluator.rb#L71, or for an association (that's not been overridden, provided completely by the factory definition), https://github.com/thoughtbot/factory_girl/blob/7e2bed5938be2e1314a7df6a647031a47d01c2c8/lib/factory_girl/attribute/association.rb#L19.
I think overloading associations, either by accepting relational objects or hashes of attributes, muddles the interface and may cause confusion. Extending this so hashes can be nested further introduces indirection here, in my opinion. I appreciate the brevity of assigning sets of attributes by passing around hashes, but again, there are mechanisms for this already built into ActiveRecord (proposed above).
Is there another mechanism that could be used here, independent of Factory Girl, which strikes a better balance? Can pure Ruby be used instead?
This still holds up, and you could take it further by having a transient attribute that's a hash and spreading it:
factory :parent do
transient do
child_attributes { { sensible_default: 'can be specified here' } }
end
association :child, **child_attributes
end
However, it doesn't give you the usual FactoryBot freedom of changing one attribute (from the default) at a time. So I think there's still a need for this if it doesn't exist already.
FWIW, this feature is built into factory_boy which was originally inspired by factory_bot. Coming from Python, I miss this feature. That project uses a double underscore syntax to traverse factory relationships, but the general concept is there.
Here is an example from the docs:
https://factoryboy.readthedocs.io/en/stable/reference.html#calling
Fields of the
SubFactory
may be overridden from the external factory:
>>> c = CompanyFactory(owner__first_name='Henry')
c.owner
<User: Henry Doe>
This would be handy for dependent attributes in the association's factory.
I got this example factory:
And in my specs I needed to set specific attributes for the
bar
object inside eachfoo
I was creating and it's really boring. The solution was something like:Maybe you (or we) could improve this to something like:
Of course, nesting more levels should be possible.