What Is the Purpose of a 'Transient Do' Block in Factorybot Factories

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

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"

What are factory_girl transient attributes? Why would I use one?

factory_bot's transient 'attributes' aren't attributes at all; they're just parameters to the factory method call that can be used by your code inside the factory. So, in your example, no, upcased isn't a model attribute.

The transient block lists 'attribute' names (that is, keys in the hash passed to the factory method) that are not attributes. factory_bot ignores them when setting attributes on the newly created model instance unless you write code in the factory definition to tell factory_bot to do something with them.

evaluator is an object passed to factory_bot callbacks. It's always the second block parameter; the model object is always the first parameter. It's conceptually like Ruby's binding. You can ask it for the value of any key in the argument hash, regardless of whether it's an actual attribute or a transient 'attribute'.

Traits and transient attributes don't affect each other as far as arguments to factory methods are concerned, since traits are scalar and transient attributes are part of the argument hash. Any number of real attributes and transient 'attributes' can be in the argument hash.

Here's the factory_bot documentation for the record: https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md

FactoryBot: how to use transient in skip_create factory to specify properties in an internal factory?

It is because you missed the curly braces {}

FactoryBot.define do
factory :experiment do
transient do
order { nil }
end
....
end
end

Factory Girl - Manipulate Transient Attribute

I was able to find a suitable solution based on the idea presented by Ben. I used a local array variable to store the types of windows that needed to be created. The traits for each window add a type of window to the array in the after(:build) block. Then, in an after(:create) block I actually create the window records.

The very first after(:build) block (the one not in a trait) is important because that resets the window_types array between object creation.

FactoryGirl.define do
window_types = []

factory :house do
floors 3
exterior 'Brick'

after(:build) do
window_types = []
end

after(:create) do |house|
window_types.each do |window_type|
FactoryGirl.create(:window, window_type, house: house)
end
end

trait :with_picture_window do
after(:build) do
window_types << :picture
end
end

trait :with_double_hung_window do
after(:build) do
window_types << :double_hung
end
end

trait :with_bay_window do
after(:build) do
window_types << :bay
end
end

factory :house_with_bay_and_picture_window, traits: [:bay, :picture]
end
end

I'm very curious to hear any thoughts on this approach. For now, this suits my needs and is very flexible.

Using a transient attribute in before(:create) callback

Defining same_group without a default value causes Factory Girl to treat it as an association rather than an attribute (and attempt to look up a factory of the same name). Changing this line to e.g. same_group nil would solve that problem. It's an admittedly confusing aspect of Factory Girl that the same method can be treated as either an attribute or an association depending on the number of arguments or presence of a block.

Defining factories with chained associations

Pass the Membership to the Comment factory in a transient attribute. In a before(:create) callback, create a Decision from the Membership and add the Decision to the Comment:

factory :comment do
transient do
membership
end

before(:create) do |comment, evaluator|
decision = create(:decision, group: evaluator.membership.group)
comment.decision = decision
end

end

Expected behavior FactoryBot

Rails caches the model instances to avoid repetitive database queries. You'll need to call

user.reload

before your assertions to update the object with the latest values from the database.



Related Topics



Leave a reply



Submit