As part of evaluating several libraries for specification testing in Ruby (MSpec, RSpec, Bacon), I wanted to benchmark the performance of the library against a simple suite of tests to see if one was particularly slower than any of the others. It wasn’t intended to be very scientific but to at least expose a slow framework, if any.
Each benchmark was performed by creating a suite of specifications based around Bacon’s whirlwind sample (consisting of 5 specs), and executed the suite 10,000 times. This benchmark test was run 5 times in order to weed out any statistical anomolies. Nb: For this analysis, I didn’t benchmark RSpec because it’s not terribly compatible with IronRuby just yet.
The results and code can be found below. In a nutshell, it does seem as though MSpec performs slower than Bacon, but when you consider that over a 50,000 test sample it was only (roughly) 10 seconds slower, the difference is negligible.
Bacon | MSpec | |
---|---|---|
Run 1 | 27.642 | 37.337 |
Run 2 | 25.598 | 37.755 |
Run 3 | 25.607 | 37.424 |
Run 4 | 25.317 | 36.439 |
Run 5 | 25.105 | 36.352 |
# This is the code for the spec test wrapper. # To execute, save the file as "spec_runner.rb" and execute # ruby spec_runner.rb # # ITERATIONS = 10000 require 'rubygems' @old_stdout = $stdout $stdout = StringIO.new def milestone(n) $stdout = @old_stdout puts ("Reached milestone: ##{n}") $stdout = StringIO.new end def time_it(&func) start_time = Time.now 1.upto(ITERATIONS) do |n| func.call milestone(n) if n % 1000 == 0 end end_time = Time.now end_time-start_time end bacon_time = time_it do load 'whirlwind_bacon.rb' end mspec_time = time_it do load 'whirlwind_mspec.rb' end $stdout = @old_stdout puts "bacon time: #{bacon_time}" puts "mspec time: #{mspec_time}"
# This is the Bacon test file. Save it as "whirlwind_bacon.rb" # # require 'bacon' describe 'A new array' do before do @ary = Array.new end it 'should be empty' do @ary.should.be.empty @ary < < 1 @ary.should.include 1 end it 'should have zero size' do @ary.size.should.equal 0 @ary.size.should.be.close 0.1, 0.5 end it 'should raise on trying fetch any index' do lambda { @ary.fetch 0 }. should.raise(IndexError). message.should.match(/out of array/) end it 'should have an object identity' do @ary.should.not.be.same_as Array.new end palindrome = lambda { |obj| obj == obj.reverse } it 'should be a palindrome' do @ary.should.be.a palindrome end end
# This is the MSpec test file. Save it as "whirlwind_mspec.rb" # # require 'mspec' describe 'A new array' do before do @ary = Array.new end it 'should be empty' do @ary.should be_empty @ary < < 1 @ary.should include(1) end it 'should have zero size' do @ary.size.should.equal 0 @ary.size.should be_close(0.1, 0.5) end it 'should raise on trying fetch any index' do d = lambda { @ary.fetch 0 } d.should raise_error(IndexError, /out of array/) end it 'should have an object identity' do @ary.should !equal(Array.new) end palindrome = lambda { |obj| obj == obj.reverse } it 'should be a palindrome' do (palindrome.call @ary).should be_true end end