---
title: Remove Duplication with FactoryBot's Traits
teaser:
tags: web,open source,testing,ruby,rails,factory_girl,factory_bot
author: Josh Clayton
published_on: 2012-05-24
---

[FactoryBot](https://github.com/thoughtbot/factory_bot)'s traits are an
outstanding way to <abbr title="Don't Repeat Yourself">DRY</abbr> up your tests and factories by naming groups of
attributes, callbacks, and associations in one concise area. Imagine defining
factories but without the attributes backed by a specific object. Here's a
basic example of a factory with two traits:

    FactoryBot.define do
      factory :todo_item do
        name { 'Pick up a gallon of milk' }

        trait :completed do
          complete { true }
        end

        trait :not_completed do
          complete { false }
        end
      end
    end

This would allow you to declare a complete or incomplete todo item very
easily:

    create(:todo_item, :completed)
    create(:todo_item, :not_completed)

Pretty handy, eh? The other way to go about this would be to have different
factories altogether for complete and incomplete:

    FactoryBot.define do
      factory :todo_item, aliases: [:incomplete_todo_item] do
        name { 'Pick up a gallon of milk' }
        complete { false }

        factory :complete_todo_item do
          complete { true }
        end
      end
    end

This may work just fine, but the problem I have with this is that any other
permutations now have to be duplicated across both paths (complete and
incomplete). What happens when todos get comments?

    FactoryBot.define do
      factory :todo_item, aliases: [:incomplete_todo_item] do
        name { 'Pick up a gallon of milk' }
        complete { false }

        factory :incomplete_todo_item_with_comments do
          after(:create) do |instance|
            create_list :comment, 2, todo_item: instance
          end
        end

        factory :complete_todo_item do
          complete { true }

          factory :complete_todo_item_with_comments do
            after(:create) do |instance|
              create_list :comment, 2, todo_item: instance
            end
          end
        end
      end
    end

This introduces duplication of creating comments because both the incomplete and
complete todo items support comment creation. The alternative keeps the
components of being complete/incomplete and having comments separate.

    FactoryBot.define do
      factory :todo_item do
        name { 'Pick up a gallon of milk' }

        trait :completed do
          complete { true }
        end

        trait :not_completed do
          complete { false }
        end

        trait :with_comments do
          after(:create) do |instance|
            create_list :comment, 2, todo_item: instance
          end
        end
      end
    end

You can mix and match traits as you please:

    create(:todo_item, :completed, :with_comments)
    create(:todo_item)
    create(:todo_item, :not_completed, name: 'Pick up a bag of sugar')

Traits make your factories much more flexible, allowing you to add groups of
attributes where needed.

Additionally, you're dealing with traits as **concepts**. This means that the
mechanics of achieving some sort of desired state are encapsulated. If the
logic of your application changed so that an item being complete was not a
boolean but rather a timestamp (for *when* the item was done), it'd be as
simple as changing the trait:

    FactoryBot.define do
      factory :todo_item do
        name { 'Pick up a gallon of milk' }

        trait :completed do
          # complete { true }
          completed_at { Time.now }
        end

        trait :not_completed do
          # complete { false }
          completed_at { nil }
        end
      end
    end

Traits are a great way to add individual pieces of state to a factory without
having to implement a massive hierarchy. [Go
forth](https://thoughtbot.github.io/factory_bot/traits/summary.html)
and use traits!

* * *

**Disclaimer:**

Looking for FactoryGirl? The library was renamed in 2017.
[Project name history can be found here.](https://github.com/thoughtbot/factory_bot/blob/master/NAME.md)
