Skip to content

Instantly share code, notes, and snippets.

@jonathanpenn
Created November 18, 2011 20:05
Show Gist options
  • Save jonathanpenn/1377594 to your computer and use it in GitHub Desktop.
Save jonathanpenn/1377594 to your computer and use it in GitHub Desktop.
An expectation framework with matchers
# This is just a fun experiment to write an expectation
# framework with matchers. NOT PRODUCTION READY! :)
def main
include Expectation
my_matchers = matchers do
define_matcher :to_be do |expected, actual|
matches? { expected == actual }
message do
"Expected %p to be equal to %p" % [actual, expected]
end
end
define_matcher :to_be_within do |args, actual|
within = args.first
expected = args[1].fetch(:of)
matches? { expected + within >= actual && expected - within <= actual }
message do
"Expected %p to be within %p of %p" % [actual, within, expected]
end
end
end
include my_matchers
expect 5, to_be(5)
expect 30, to_be_within(2, of: 21)
end
module Expectation
def matchers &block
mod = Module.new
mod.extend MatcherBuilder
mod.class_eval &block
mod
end
def expect value, matcher
matcher.match_to value
raise MatchError.new(matcher.message) unless matcher.matches?
end
class MatchError < StandardError; end
module MatcherBuilder
def define_matcher name, &definition
define_method name do |*expected|
expected = expected.first if expected.length == 1
MatcherContainer.new expected, &definition
end
end
end
class MatcherContainer
class DefinitionProxy
attr_reader :matches_block, :message_block
def matches? &block
@matches_block = block
end
def message &block
@message_block = block
end
end
def initialize expected, &definition
@expected = expected
@definition = definition
end
def match_to actual
@proxy = DefinitionProxy.new
@proxy.instance_exec(@expected, actual, &@definition)
end
def matches?
@proxy.matches_block.call
end
def message
@proxy.message_block.call
end
end
end
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment