Never Fear, Traits are Here

Josh Clayton

I can’t count the number of times I’ve worked on Rails apps where I’m using Factory Bot and want to declare a factory with multiple parents. We’ve all been there, don’t lie.

    FactoryBot.define do
      factory :user do
        name { "Friendly User" }

        factory :admin do
          admin { true }
        end

        factory :male_user do
          gender { "Male" }
          name   { "John Doe" }

          factory :male_admin_user do
            admin { true }
          end

          factory :teenage_male_user do
            date_of_birth { 15.years.ago }

            factory :teenage_admin_male_user do
              admin { true }
            end
          end
        end

        factory :female_user do
          gender { "Female" }
          name   { "Jane Doe" }

          factory :female_admin_user do
            admin { true }
          end

          factory :teenage_female_user do
            date_of_birth { 15.years.ago }

            factory :teenage_admin_female_user do
              admin { true }
            end
          end
        end
      end
    end

Oh, the duplication! This isn’t DRY.

Factory Bot 2.0.41 introduces traits. I’ll cut to the chase:

    FactoryBot.define do
      factory :user do
        name { "Friendly User" }

        trait :admin do
          admin { true }
        end

        trait :male do
          gender { "Male" }
          name   { "John Doe" }
        end

        trait :female do
          gender { "Female" }
          name   { "Jane Doe" }
        end

        trait :teenager do
          date_of_birth { 15.years.ago }
        end

        factory :male_user,                 :traits => [:male]
        factory :female_user,               :traits => [:female]
        factory :teenage_male_user,         :traits => [:teenager, :male]
        factory :teenage_female_user,       :traits => [:teenager, :female]
        factory :male_admin_user,           :traits => [:admin, :male]
        factory :female_admin_user,         :traits => [:admin, :female]
        factory :teenage_admin_male_user,   :traits => [:teenager, :admin, :male]
        factory :teenage_admin_female_user, :traits => [:teenager, :admin, :female]
      end
    end

Mix and match however you please. Bonus: you can assign traits like plain-old attributes.

    FactoryBot.define do
      factory :user do
        name { "Friendly User" }

        trait :admin do
          admin { true }
        end

        trait :male do
          gender { "Male" }
          name   { "John Doe" }
        end

        factory :male_admin_user do
          male
          admin
        end
      end
    end

Many props go out to Thomas Walpole, who helped get a lot of this written; thanks Thomas!

So go ahead, get your trait on; you’ll thank me later.

Project name history can be found here.


  1. FactoryGirl was renamed to FactoryBot in 2017. 

We've been helping engineering teams deliver exceptional products for over 20 years. Our designers, developers, and product managers work closely with teams to solve your toughest software challenges through collaborative design and development. Learn more about us.