Module: DiscreteEvent::FakeRand

Defined in:
lib/discrete_event/fake_rand.rb

Overview

A utility for testing objects that use the built-in Ruby pseudorandom number generator (Kernel::rand); use it to specify a particular sequence of (non-random) numbers to be returned by rand.

Using this utility may be better than running tests with a fixed seed, because you can specify random numbers that produce particular behavior.

The sequence is specific to the object that you give to FakeRand.for; this means that you must specify a separate fake sequence for each object in the simulation (which is usually easier than trying to specify one sequence for the whole sim, anyway).

Examples:

class Foo
  def do_stuff
    # NB: FakeRand.for won't work if you write "Kernel::rand" instead of
    # just "rand" here.
    puts rand
  end
end
foo = Foo.new
foo.do_stuff # outputs a pseudorandom number
DiscreteEvent::FakeRand.for(foo, 0.0, 0.1)
foo.do_stuff # outputs 0.0
foo.do_stuff # outputs 0.1
foo.do_stuff # raises an exception

Class Method Summary collapse

Class Method Details

.for(object, *fakes) ⇒ nil

Create a method rand in object's singleton class that returns the given fake “random numbers;” it raises an error if it runs out of fakes.

Parameters:

  • object (Object)

    to modify

  • fakes (Array)

    sequence of numbers to return

Returns:

  • (nil)

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/discrete_event/fake_rand.rb', line 41

def self.for(object, *fakes)
  undo_for(object) # in case rand is already faked
  (class << object; self; end).instance_eval do
    define_method :rand do |*args|
      raise 'out of fake_rand numbers' if fakes.empty?
      r = fakes.shift

      # can be either the rand() or rand(n) form
      n = args.shift || 0
      if n.zero?
        r
      else
        (r * n).to_i
      end
    end
  end
end

.undo_for(object) ⇒ nil

Reverse the effects of for. If object has its own rand, it is restored; otherwise, the object goes back to using Kernel::rand.

Parameters:

  • object (Object)

    to modify

Returns:

  • (nil)

67
68
69
70
71
72
# File 'lib/discrete_event/fake_rand.rb', line 67

def self.undo_for(object)
  return unless object.methods.map(&:to_s).member?('rand')
  (class << object; self; end).instance_eval do
    remove_method :rand
  end
end