What is the purpose of a `transient do` block in FactoryBot factories?
Asked Answered
L

2

86

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:
Using FactoryGirl to easily create complex data sets in 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?

Legator answered 25/7, 2016 at 16:34 Comment(1)
Your example doesn't use transient. Do you have a factory that uses transient that you want to understand?Agamemnon
M
161

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"
Menfolk answered 13/1, 2017 at 3:23 Comment(0)
A
14

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. In both cases, they let a test create an object by expressing concepts (similar to a trait), without knowing anything about the implementation. These two examples are easy to understand, without knowing any object internals:

FactoryBot.create(:car, make: 'Saturn', accident_count: 3)
FactoryBot.create(:car, make: 'Toyota', unsold: true)

These two examples use transients for in distinct ways:

  • Controlling/altering the creation of related objects (e.g. accident_count).
  • Altering values assigned to other attribute assignments (e.g. unsold).

Inside the factory, those two transients are used like this:

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

The two attributes in the create call (accident_count and unsold) are not part of the model and are not explicitly saved...but both have effects on the resulting objected created for the test. That is the essential distinction of a transient, vs a trait or explicit attribute value.

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.

Aerate answered 21/3, 2019 at 19:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.