Created
July 14, 2011 01:16
-
-
Save joshed-io/1081685 to your computer and use it in GitHub Desktop.
Easiest possible RSpec Performance Test w/ Scenarios
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# here's a quick recipe to run a performance test | |
# with this method, you can: | |
#-> easily choose the examples you want included from your existing specs | |
#-> define the target number of total iterations you'd like, no limit | |
#-> tune the transaction mix by specifying frequency metadata for each example | |
#-> be happy that the transaction mix is fairly homogeneous over the test interval | |
# (ideally you'd run this with acceptance (webrat/capybara) specs, but you | |
# could employ this technique for any rspec test) | |
# note: there's no measuring or performance reporting here, but you can capture | |
# it all in New Relic (enable monitor_mode) and run a time-based report when | |
# your test is complete. | |
# First, THE DSL | |
# From your test suite, pick a subset of examples useful for | |
# generating load, and assign a perf key and optionally a 'frequency' of execution | |
it "should be fast", :perf => true, :frequency => 0.8 do | |
visit "/home" #using capybara syntax for this example | |
page.should have_content("i'm ready already!") | |
end | |
# now, it's time to make rspec aware of these values | |
#somewhere in your spec_helper.rb, place this: | |
RSpec.configure do |config| | |
#only run examples with 'perf' specified in metadata | |
config.filter_run(:perf => true) | |
#if your performance scenario calls for 'wait time', do it here. e.g. | |
config.after(:each) do | |
#sleep(example.metadata[:wait_time]) | |
end | |
end | |
# note that you'll want to include this block when you want to run a | |
# performance test, so you might create a separate RAILS/RACK_ENV or | |
# conditionally include the block based on an environment variable | |
# ---------------------- | |
#now for the main RSpec patch - no guarantees and I'm sure there's | |
#a better outpatient way to do this. but for now, scapel! | |
#copy this anywhere code is required after Rspec is loaded, | |
#your spec_helper for example | |
#this is helper to figure out example amount + frequency | |
#examples without frequency specified default to 1 | |
def calculate_iterations(options) | |
return 0 unless options[:perf] #filtering here improves performance | |
iter_ct = (ENV['ITER'] ? ENV['ITER'].to_i : 1000) | |
(((iter_ct.to_f * (options[:frequency] || 1.0))+1).to_i) | |
end | |
#patch rspec example accumulation to adjust example | |
#levels to the right amount and frequency | |
### begin derdy patch, helmet required ### | |
module RSpec | |
module Core | |
class ExampleGroup | |
def self.define_example_method(name, extra_options={}) | |
module_eval(<<-END_RUBY, __FILE__, __LINE__) | |
def self.#{name}(desc=nil, *args, &block) | |
options = build_metadata_hash_from(args) | |
options.update(:pending => true) unless block | |
options.update(#{extra_options.inspect}) | |
calculate_iterations(options).times do | |
examples << RSpec::Core::Example.new(self, desc, options, block) | |
end | |
#shuffle examples for good perf simulation | |
examples.shuffle! | |
examples.last | |
end | |
END_RUBY | |
end | |
define_example_method :example | |
class << self | |
alias_method :alias_example_to, :define_example_method | |
end | |
alias_example_to :it | |
alias_example_to :specify | |
alias_example_to :focused, :focused => true, :focus => true | |
alias_example_to :focus, :focused => true, :focus => true | |
alias_example_to :pending, :pending => true | |
alias_example_to :xit, :pending => true | |
end | |
end | |
end | |
# ----------- | |
# that's it for the rubies. | |
# To run: Specify the TOTAL number of example executions | |
# you'd like as an environment variable | |
# ** from your shell ** | |
# ITER=1000 bundle exec rake spec:acceptance #or whatever your test command is | |
# ******************* | |
# the ITER is multiplied with frequency from metadata to determine | |
# how many of each example get run. e.g. the example | |
it "should run a few times", :perf => true, :frequency => 0.5 do ... end | |
# paired with the command | |
# ITER=100 bundle exec rake spec:acceptance | |
# will run ITER*frequency times, or 50 in this case. The number of iterations | |
# of each example is calculated independently, so you can easily go over | |
# (i.e. its up to you to make sure your frequencies add up to 1.0) | |
# to scale up your load, you'll have to start more RSpec processes, or | |
# run this simultaneously across machines. As long as your examples are | |
# idempotent (and they are, right?), this shouldn't be a problem. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment