Module: Flores::RSpec::Stress

Defined in:
lib/flores/rspec/stress.rb

Overview

This module adds helpers useful in doing stress testing within rspec.

The number of iterations in a stress test is random.

By way of example, let’s have a silly test for adding two positive numbers and expecting the result to not be negative:

   describe "Addition" do
     context "of two positive numbers" do
       let(:a) { Flores::Random.number(1..10000) }
       let(:b) { Flores::Random.number(1..10000) }
       subject { a + b }

       # Note the use of 'stress_it' here!
       stress_it "should be greater than zero" do
         expect(subject).to(be > 0)
       end
     end
   end

Running this:

   % rspec 
   <lots of dots>

   Finished in 0.45412 seconds (files took 0.32963 seconds to load)
   4795 examples, 0 failures

In this way, instead of testing 1 fixed case or 1 randomized case, we test
*many* cases in one rspec run.

Instance Method Summary collapse

Instance Method Details

#stress_it(name, *args, &block) ⇒ Object

Generate a random number of copies of a given example. The idea is to take 1 ‘it` and run it N times to help tease out failures. Of course, the teasing requires you have randomized `let` usage, for example:

let(:number) { Flores::Random.number(0..200) }
it "should be less than 100" do
  expect(number).to(be < 100)
end

This creates N (random) copies of your spec example. Using ‘stress_it` is preferred instead of `stress_it_internal` because this method will cause before, after, and around clauses to be invoked correctly.



101
102
103
104
105
# File 'lib/flores/rspec/stress.rb', line 101

def stress_it(name, *args, &block)
  Flores::Random.iterations(Flores::RSpec.iterations).each do
    it(name, *args, &block)
  end # each
end

#stress_it_internal(name, options = {}, &block) ⇒ Object

Wraps ‘it` and runs the block many times. Each run has will clear the `let` cache.

The implementation of this is roughly that the given block will be run N times within an ‘it`:

stress_it_internal "my test" do
   expect(...)
 end

is roughly equivalent to

it "my test" do
  1000.times do
    expect(...)
    __memoized.clear
  end
end

The intent of this is to allow randomized testing for fuzzing and stress testing of APIs to help find edge cases and weird behavior.

The default number of iterations is randomly selected between 1 and 1000 inclusive



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/flores/rspec/stress.rb', line 73

def stress_it_internal(name, options = {}, &block)
  stress__iterations = Flores::Random.iterations(options.delete(:stress_iterations) || Flores::RSpec::DEFAULT_ITERATIONS)
  it(name, options) do
    # Run the block of an example many times
    stress__iterations.each do
      # Run the block within 'it' scope
      instance_eval(&block)

      # clear the internal rspec `let` cache this lets us run a test
      # repeatedly with fresh `let` evaluations.
      # Reference: https://github.com/rspec/rspec-core/blob/5fc29a15b9af9dc1c9815e278caca869c4769767/lib/rspec/core/memoized_helpers.rb#L124-L127
      __memoized.clear
    end
  end # it ...
end