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.