Factory Bot 2.2, your new best friend!

Josh Clayton

Aside from a handful of internal code clean up, Factory Bot 2.2 brings a handful of awesomeness to the table. Factory Bot 2.1 was released a mere month and a half ago but loads of improvements have been added since then. Some key features:

Attributes from traits can be modified

When we introduced traits, there were a couple of caveats: you couldn’t override traits’ attributes from child factories, and you couldn’t overwrite traits’ attributes when using FactoryBot.modify. Not the case anymore! We’ve been hammering away at Factory Bot’s internals, cleaning things up and delaying factory resolution as late as possible. This allows for:

FactoryBot.define do
  factory :user do
    name { "Stranger" }

    trait :male do
      name { "Jon Snow" }
      gender { "Male" }
    end

    factory :male_user do
      male
    end

    factory :brandon do
      male
      name { "Brandon" }
    end
  end
end

FactoryBot.modify do
  factory :brandon do
    name { "Brandon Stark" }
  end
end

Parent factories can be defined after their children

This may sound a bit odd. Why would you ever want to define a parent factory after a child factory? When you’ve split your factories up across different files and don’t want to prefix file names in order for the files to be loaded in a specific order.

Factory Bot looks in six places by default: factories/, factories.rb, spec/factories/, spec/factories.rb, test/factories/, and test/factories.rb. If you’ve split up your factories within one of the directories, that’s wonderful; however, what if you want a “base” factory file and multiple factory files for the various children? Before 2.2, you’d have to name that base file in order to force it to be loaded before children, and that’s just gross.

Transient attributes have a cleaner syntax

Transient attributes are a relatively new feature allowing attributes to be passed via a hash when creating/building that don’t actually interact with the instance (or hash, when using attributes_for). The old syntax required calling #ignore on individual attribute declarations, which didn’t really look great. Now, you define all your ignored attributes in a block.

FactoryBot.define do
  factory :user do
    ignore do
      rockstar { true }
      four     { 2 + 2 }
    end

    name { "John Doe#{" - Rockstar" if rockstar}" }
  end
end

The old ignore syntax has been deprecated and will be removed in 3.0.

Factories now accept blocks and yield the result

Have you ever done this?

let(:old_published_post) do
  create(:post, created_at: 2.years.ago).tap do |post|
    post.publish!
  end
end

Now, you can just pass a block to the syntax methods (create, build, build_stubbed, and attributes_for) and they’ll yield themselves; no more tap! It’s a simple enough change, but four characters fewer is four characters fewer.

What’s next for Factory Bot

Aside from getting in solid pull requests, we’re going to be reworking the test suite so it’s much more flexible and continue reworking the inner workings of the gem. There’s currently a handful of code surrounding how to evaluate attributes and the order in which we should do so that we’d like to remove completely and have attributes resolve lazily.

We’re also considering a new syntax method, decorate, which would allow for:

FactoryBot.define do
  factory :post do
    title { "My great post!" }
    body { "An awesome story" }

    decorate :old do
      title { "How is this even around still?" }
      created_at { 2.years.ago }
    end

    decorate :archived do
      title { "This shouldn't be on the homepage" }
      archived { true }

      decorate :unpublished do
        published { false }
      end
    end
  end
end

This would give us four factories: post, old_post, archived_post, and archived_unpublished_post.

What features would you like to see in Factory Bot? Post them on the GitHub issues page and let us know in the comments!


Disclaimer:

Looking for FactoryGirl? The library was renamed in 2017. Project name history can be found here.