Profiling RSpec 2 Examples

Tests can be slow. This is how to find out why they’re slow.

Toss this bad boy into spec/support/profile.rb and tag any example with :profile => true and it’ll spit out callgrind dumps for your consumption in KCachegrind or similar.

If you specify PROFILE=all on your command line, it’ll profile all examples, regardless of tagging. If you pass PROFILE=true (or any other non-nil, non-ALL value) then it’ll profile tagged examples.

RSpec.configure do |c|
  def profile
    result = RubyProf.profile { yield }
    name = example.metadata[:full_description].downcase.gsub(/[^a-z0-9_-]/, "-").gsub(/-+/, "-")
    printer = RubyProf::CallTreePrinter.new(result)
    open("tmp/performance/callgrind.#{name}.#{Time.now.to_i}.trace", "w") do |f|
      printer.print(f)
    end
  end

  c.around(:each) do |example|
    if ENV['PROFILE'] == 'all' or (example.metadata[:profile] and ENV['PROFILE'])
      profile { example.run }
    else
      example.run
    end
  end
end

Then add the tag to your tests:

describe "A test" do
  it "can be profiled", :profile => true do
    expect { some_complex_operation }.to_not raise_error
  end
end

Bam.