How to create multi trait inside trait in FactoryBot
When working within test fixtures within Ruby on Rails applications, the de facto standard has become FactoryBot. The fact that it can define factories that are composed of several traits and behavior makes it highly usable. Another very robust but sometimes overstated factor that makes it really exceptional is the ability to include traits within other traits. This has the potential to really be helpful to organize and reuse within your test fixture setup.
Understanding factory traits
Before diving into nested traits, let's quickly review what traits are in FactoryBot
. Traits allow you to group attributes together under a meaningful name, making your factories more expressive and your tests more readable.
FactoryBot.define do
factory :user do
first_name { "John" }
last_name { "Doe" }
trait :admin do
admin { true }
access_level { 10 }
end
end
end
With this factory, you can create a regular user with create(:user)
or an admin user with create(:user, :admin)
.
Nesting traits within traits: Traitception!
The real magic happens when you start nesting traits within other traits. This allows you to build more complex objects with cleaner, more maintainable code. Consider a blog application where posts can be in various states:
FactoryBot.define do
factory :post do
title { "Sample Post" }
content { "This is sample content." }
published_at { nil }
trait :published do
published_at { Time.current }
end
trait :deleted do
deleted_at { Time.current }
end
trait :deleted_by_admin do
deleted #### This references the :deleted trait!
deleted_by { :admin }
end
trait :featured_published do
published #### This references the :published trait!
featured { true }
end
# next sample
trait :deleted_published do
deleted #### reference to another trait
published #### reference to another trait
end
end
end
In this example, the deleted_by_admin
trait includes the deleted
trait, which sets deleted_at
to the current time. Similarly, the featured_published
trait includes the published
trait.
So now you can imagine and combine traits in various ways and mix multiple ways:
# Creating a super admin user with a profile
create(:user, :super_admin, :with_profile)
# Creating a featured, published post that's been deleted by an admin
# (though this combination might not make logical sense)
create(:post, :featured_published, :deleted_by_admin)
Final thoughts
Nested traits in FactoryBot provide a powerful way to organize and compose your test data. By referencing traits within other traits, you can build complex objects with clear, maintainable code. This approach follows the DRY principle and makes your test data setup more expressive and easier to understand.
Next time you find yourself with similar groups of attributes across multiple traits, consider using trait nesting to improve your factories.
Happy traitcepting!