What is the purpose of transient do
in FactoryBot factories?
I've seen a lot of factories that begin with something like below.
factory :car do
owner nil
other_attribute nil
end
...
I've found some information on this blog: http://blog.thefrontiergroup.com.au/2014/12/using-factorygirl-easily-create-complex-data-sets-rails/
But I still don't fully understand how and why to do this. My experience with FactoryBot is minimal.
Could anyone with some experience using FactoryBot share some insight?
transient
attributes allow you to pass in data that isn’t an attribute on the model.
Say you have a model called car
with the following attributes:
- name
- purchase_price
- model
You want to capitalize the name of the car when you create the car model in the factory. What we can do is:
factory :car do
transient do
# capitalize is not an attribute of the car
capitalize false
end
name { "Jacky" }
purchase_price { 1000 }
model { "Honda" }
after(:create) do |car, evaluator|
car.name.upcase! if evaluator.capitalize
end
end
Hence, whenever you create the car factory and you want to capitalize the name. You can do
car = FactoryGirl.create(:car, capitalize: true)
car.name
# => "JACKY"
Hope it helps.
Transient attributes are essentially variables local to the factory that do not persist into the created object.
I have seen two main uses of transient attributes:
- Controlling/altering the creation of related objects (e.g. accident_count).
- Altering values assigned to other attribute assignments (e.g. unsold).
You could, of course, use these transient attributes for anything else you need to code during the object creation.
factory :car do
transient do
accident_count 0
unsold false
end
owner unsold ? 'new inventory' : nil
after(:create) do |car, evaluator|
create_list(:police_report, evaluator.accident_count, vehicle: car)
end
end
This lets your test express a concept (similar to a trait), without knowing anything about the implementation:
FactoryBot.create(:car, make: 'Saturn', accident_count: 3)
FactoryBot.create(:car, make: 'Toyota', unsold: true)
IMO, I would stick with traits when they work (e.g. unsold, above). But when you need to pass a non-model value (e.g. accident_count), transient attributes are the way to go.