FactoryBot 3.2 brings a slew of new features; while I’d considered breaking it all up into 3.2 and 3.3 releases, I decided to bundle them for MORE AWESOMENESS during RailsConf.
To install, add (or change) your Gemfile:
gem 'factory_bot_rails', '~> 3.2.0'
So, what does FactoryBot 3.2 give you?
Implicitly Call FactoryBot from Dynamic Attributes
Dynamic attributes are attributes you declare on a factory that are defined with a block to be evaluated every time the factory is run.
factory :user do
name { NameGenerator.generate }
end
There have been a handful of times where I want to share a sequence or create a record inside of an attribute, but who wants to do this?
sequence(:long_string) {|n| "#{LoremIpsum.generate}#{n}" }
factory :user do
name { FactoryBot.generate(:long_string) }
end
Why type out FactoryBot.
in the block? In FactoryBot 3.2, you don’t have to:
sequence(:long_string) {|n| "#{LoremIpsum.generate}#{n}" }
factory :user do
name { generate(:long_string) }
end
If it just so happens that if you have a method named generate
on your object and it’s colliding with FactoryBot, you can still use the explicit FactoryBot.generate(:long_string)
.
Deprecate Alternate Syntaxes
We’ve deprecated alternate syntaxes. This means if you’re using the object_daddy
or machinist
syntax, you’ll be urged to upgrade to the FactoryBot 2 syntax. Support of the different syntaxes will be removed in FactoryBot 4.0.
Why are we doing this?
We’re confident that the FactoryBot 2 syntax is better than any other syntax out there. It’s clear, concise, and is incredibly flexible. It allows for usage of traits and other benefits we’ve added to the default syntax.
Skip the to_create
Block Easily
Certain factories may skip the to_create
block altogether (which normally calls #save!
). Instead of calling to_create { }
on the factory, you can now call skip_create
.
factory :user do
skip_create
end
Slim Down initialize_with
If you use initialize_with
, you’ll know you can define your own construction method instead of just calling new
without arguments. This becomes tedious, however, when you constantly repeate the build class.
We’ve created a shorthand for calling new
within initialize_with
:
factory :user do
name { "John Doe" }
initialize_with { new(name) }
end
Other class methods won’t be supported, but the common case (new
) is covered for you!
Register Custom Build Strategies
Ever wish you could call FactoryBot.json(:user)
to get a JSON representation of your factory? Now you can. Ever wish attributes_for
would build your association data instead of ignoring associations altogether? You can overwrite FactoryBot’s implementation of attributes_for
with your own code.
Here’s an example of registering your own JSON strategy, decorating the currently registered create strategy with additional behavior.
class JsonStrategy
def initialize
@strategy = FactoryBot.strategy_by_name(:create).new
end
delegate :association, to: :@strategy
def result(evaluation)
@strategy.result(evaluation).to_json
end
end
To register the new strategy, run
FactoryBot.register_strategy(:json, JsonStrategy)
Once registered, you can refer to it like so:
FactoryBot.json(:user)
I recommend digging through FactoryBot’s current implementations of build
, create
, build_stubbed
, and attributes_for
, which use FactoryBot’s brand new register_strategy
interface.
Add ActiveSupport::Notifications Instrumentation
Now that we’ve dropped Rails 2 support, we can start using some of the awesome features of Rails 3. This includes ActiveSupport::Notifications
, which uses pub/sub and allows us to track when factories get run.
Want to see which factories take longer than half a second to run? If you’re using RSpec, you could add this to the before(:suite)
block:
ActiveSupport::Notifications.subscribe("factory_bot.run_factory") do |name, start, finish, id, payload|
execution_time_in_seconds = finish - start
if execution_time_in_seconds >= 0.5
$stderr.puts "Slow factory: #{payload[:name]} using strategy #{payload[:strategy]}"
end
end
Any slow factories will be written out to STDERR.
The other use case that I’ve wanted for a long time is keeping track of what factories I’m creating and the strategies used. Creating a record is obviously going to be significantly slower than building a stubbed version of it, so printing out a table of all the factories (and the number of times each strategy is executed for that factory) should be fairly straightforward. Feel free to dig into FactoryBot’s acceptance tests to see a couple of straightforward uses.
What’s next
As we continue to move forward with FactoryBot, we’re planning on making it easier to use initialize_with
. I’d also love to see a pull request or example of generating a table of factories and strategy count (much like rake stats
).
Disclaimer:
Looking for FactoryGirl? The library was renamed in 2017. Project name history can be found here.