FactoryGirl 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_girl_rails', '~> 3.2.0'
So, what does FactoryGirl 3.2 give you?
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 { FactoryGirl.generate(:long_string) }
end
Why type out FactoryGirl. in the block? In FactoryGirl 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 FactoryGirl, you can still use the explicit
FactoryGirl.generate(:long_string).
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
FactoryGirl 2 syntax. Support of the different syntaxes will be removed in
FactoryGirl 4.0.
Why are we doing this?
We’re confident that the FactoryGirl 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.
to_create Block EasilyCertain 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
initialize_withIf 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!
Ever wish you could call FactoryGirl.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 FactoryGirl’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 = FactoryGirl.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
FactoryGirl.register_strategy(:json, JsonStrategy)
Once registered, you can refer to it like so:
FactoryGirl.json(:user)
I recommend digging through FactoryGirl’s current implementations of build,
create, build_stubbed, and attributes_for, which use FactoryGirl’s brand
new register_strategy interface.
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_girl.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 FactoryGirl’s acceptance tests to see a couple of straightforward uses.
As we continue to move forward with FactoryGirl, 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).