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

Ruby on-RailsRubyRspecFactory Bot

Ruby on-Rails Problem Overview

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

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?

Ruby on-Rails Solutions

Solution 1 - Ruby on-Rails

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

  name           { "Jacky" }
  purchase_price { 1000 }
  model          { "Honda" }
  after(:create) do |car, evaluator|
    car.name.upcase! if evaluator.capitalize

Hence, whenever you create the car factory and you want to capitalize the name. You can do

car = FactoryGirl.create(:car, capitalize: true)
# => "JACKY"

Hope it helps.

Solution 2 - Ruby on-Rails

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

  owner unsold ? 'new inventory' : nil

  after(:create) do |car, evaluator|
    create_list(:police_report, evaluator.accident_count, vehicle: car) 

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.


All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questiontim_xyzView Question on Stackoverflow
Solution 1 - Ruby on-RailsSam Kah ChiinView Answer on Stackoverflow
Solution 2 - Ruby on-RailsDavid HempyView Answer on Stackoverflow