1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
|
# frozen_string_literal: true
require 'test/unit'
require 'ffaker'
# Helpers for checking if a method is deterministic -- e.g., that the Random
# results are repeatable given the same random seed.
module DeterministicHelper
def self.included(base)
base.extend ClassMethods
end
# Accepts a block. Executes the block multiple times after resetting
# the internal Random Number Generator state and compared the results of
# each execution to make sure they are the same.
def assert_deterministic(options = {}, &block)
raise ArgumentError, 'Must pass a block' unless block
options = { message: 'Results are not repeatable' }.merge(options)
returns = Array.new(2) do
FFaker::Random.reset!
Array.new(5, &block)
end
assert(returns.uniq.length == 1, options[:message])
end
%w[< > <= >=].each do |operator|
operator_name =
case operator[0]
when '<' then 'less_than'
when '>' then 'greater_than'
else raise 'Unexpected operator'
end
operator_name += '_or_equal_to' if operator[1] == '='
define_method "assert_#{operator_name}" do |got, expected|
assert(
got.public_send(operator, expected),
"Expected #{operator} \"#{expected}\", but got #{got}"
)
end
end
def assert_between(got, range)
assert_greater_than_or_equal_to got, range.begin
public_send "assert_less_than#{'_or_equal_to' unless range.exclude_end?}", got, range.end
end
def assert_random(original_block, *args)
100.times do
yield(*args)
assert_deterministic(&original_block)
end
end
%w[less_than_or_equal_to between].each do |method_name|
define_method "assert_random_#{method_name}" do |*args, &block|
assert_random(block) { send "assert_#{method_name}", block.call, *args }
end
end
# Methods to be called outside of individual examples.
module ClassMethods
# Shorthand method to quickly test the determinability of multiple methods.
#
# assert_methods_are_deterministic(
# <faker module under test>,
# <method names to test, as array of symbols>
# )
#
# assert_methods_are_deterministic(
# FFaker::SomeModule,
# :some_method, :some_other_method
# }
def assert_methods_are_deterministic(klass, *methods)
Array(methods).each do |meth|
define_method "test_#{meth}_is_deterministic" do
assert_deterministic(message: "Results from `#{klass}.#{meth}` are not repeatable") do
klass.send(meth)
end
end
end
end
end
end
|