Faster RSpec: Profiling FactoryGirl

One way to speed up a slow test suite is to optimize how FactoryGirl is used, but where do you focus your efforts?

Before tuning your factory usage, consider measuring which factories are adding the most time to your test runs. You may discover the time spent in factories is fine and decide to speed up another area of your suite.

Introduce FactoryGirlProfiler below to an RSpec project. It will only report when the --profile/-p flag is passed to the rspec command:

# File: spec/support/factory_girl_profiler.rb
# Ensure this file is required by spec_helper.rb or rails_helper.rb
class FactoryGirlProfiler

  attr_accessor :results

  def self.setup
    profiler = self.new

    RSpec.configure do |config|
      config.before(:suite) { profiler.subscribe }
      config.after(:suite) { profiler.dump }
    end
  end

  def initialize
    self.results = {}
  end

  def subscribe
    ActiveSupport::Notifications.subscribe("factory_girl.run_factory") do |name, start, finish, id, payload|
      factory, strategy = payload.values_at(:name, :strategy)

      factory_result = results[factory] ||= {}
      strategy_result = factory_result[strategy] ||= { duration_in_secs: 0.0, count: 0 }

      duration_in_secs = finish - start
      strategy_result[:duration_in_secs] += duration_in_secs
      strategy_result[:count] += 1
    end
  end

  def dump
    puts "\nFactoryGirl Profiles"
    total_in_secs = 0.0
    results.each do |factory_name, factory_profile|
      puts "\n  #{factory_name}"
      factory_profile.each do |strategy, profile|
        puts "    #{strategy} called #{profile[:count]} times took #{profile[:duration_in_secs].round(2)} seconds total"
        total_in_secs += profile[:duration_in_secs]
      end
    end
    puts "\n Total FactoryGirl time #{total_in_secs.round(2)} seconds"
  end

end

RSpec.configure do |config|
  config.add_setting :profile_factories, default: false
  config.profile_factories = config.profile_examples? || ARGV.include?('--profile') || ARGV.include?('-p')
  FactoryGirlProfiler.setup if config.profile_factories?
end

Source: https://github.com/eliotsykes/rails-testing-toolbox/blob/master/factory_girl_profiler.rb

Now run rspec --profile and review the output to find results formatted like below:

FactoryGirl Profiles

  invoice
    create called 4 times took 0.18 seconds total

  product
    build called 7 times took 0.07 seconds total
    create called 17 times took 0.22 seconds total

  user
    build called 12 times took 0.06 seconds total
    build_stubbed called 2 times took 0.01 seconds total
    create called 28 times took 1.51 seconds total

 Total FactoryGirl time 2.05 seconds

This will show the factories that are taking up the most time and help you decide where to focus your optimization work.