Initial work on have_received matcher

This commit is contained in:
Michael Miller 2019-10-13 18:41:10 -06:00
parent 8cbe2edf30
commit d422376aaf
5 changed files with 60 additions and 10 deletions

View file

@ -1,8 +1,10 @@
require "./method_stub" require "./generic_method_call"
require "./generic_method_stub"
module Spectator module Spectator
abstract class Double abstract class Double
@spectator_stubs = Deque(MethodStub).new @spectator_stubs = Deque(MethodStub).new
@spectator_stub_calls = Deque(MethodCall).new
private macro stub(definition, &block) private macro stub(definition, &block)
{% {%
@ -27,7 +29,7 @@ module Spectator
{% if name.ends_with?('=') && name.id != "[]=" %} {% if name.ends_with?('=') && name.id != "[]=" %}
def {{name}}(arg) def {{name}}(arg)
call = ::Spectator::MethodCall.new({{name.symbolize}}, {arg}, NamedTuple.new) call = ::Spectator::GenericMethodCall.new({{name.symbolize}}, {arg}, NamedTuple.new)
stub = @spectator_stubs.find(&.callable?(call)) stub = @spectator_stubs.find(&.callable?(call))
if stub if stub
stub.as(::Spectator::GenericMethodStub(typeof(%method(arg)))).call(call) stub.as(::Spectator::GenericMethodStub(typeof(%method(arg)))).call(call)
@ -37,7 +39,7 @@ module Spectator
end end
{% else %} {% else %}
def {{name}}(*args, **options){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %} def {{name}}(*args, **options){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
call = ::Spectator::MethodCall.new({{name.symbolize}}, args, options) call = ::Spectator::GenericMethodCall.new({{name.symbolize}}, args, options)
stub = @spectator_stubs.find(&.callable?(call)) stub = @spectator_stubs.find(&.callable?(call))
if stub if stub
stub.as(::Spectator::GenericMethodStub(typeof(%method(*args, **options)))).call(call) stub.as(::Spectator::GenericMethodStub(typeof(%method(*args, **options)))).call(call)
@ -48,7 +50,7 @@ module Spectator
{% if name != "[]=" %} {% if name != "[]=" %}
def {{name}}(*args, **options){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %} def {{name}}(*args, **options){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
call = ::Spectator::MethodCall.new({{name.symbolize}}, args, options) call = ::Spectator::GenericMethodCall.new({{name.symbolize}}, args, options)
stub = @spectator_stubs.find(&.callable?(call)) stub = @spectator_stubs.find(&.callable?(call))
if stub if stub
stub.as(::Spectator::GenericMethodStub(typeof(%method(*args, **options) { |*yield_args| yield *yield_args }))).call(call) stub.as(::Spectator::GenericMethodStub(typeof(%method(*args, **options) { |*yield_args| yield *yield_args }))).call(call)
@ -79,5 +81,9 @@ module Spectator
protected def spectator_define_stub(stub : MethodStub) : Nil protected def spectator_define_stub(stub : MethodStub) : Nil
@spectator_stubs << stub @spectator_stubs << stub
end end
protected def spectator_stub_calls(method : Symbol) : Array(MethodCall)
@spectator_stub_calls.select { |call| call.name == method }
end
end end
end end

View file

@ -683,6 +683,11 @@ module Spectator
expect {{block}}.to raise_error({{type}}, {{message}}) expect {{block}}.to raise_error({{type}}, {{message}})
end end
macro have_received(method)
%test_value = ::Spectator::TestValue.new(({{method.id.symbolize}}), {{method.id.stringify}})
::Spectator::Matchers::ReceiveMatcher.new(%test_value)
end
# Used to create predicate matchers. # Used to create predicate matchers.
# Any missing method that starts with 'be_' or 'have_' will be handled. # Any missing method that starts with 'be_' or 'have_' will be handled.
# All other method names will be ignored and raise a compile-time error. # All other method names will be ignored and raise a compile-time error.

View file

@ -0,0 +1,13 @@
require "./method_call"
module Spectator
class GenericMethodCall(T, NT) < MethodCall
getter args : T
getter options : NT
def initialize(name : Symbol, @args : T, @options : NT)
super(name)
end
end
end

View file

@ -0,0 +1,30 @@
require "../double"
require "./standard_matcher"
module Spectator::Matchers
struct ReceiveMatcher < StandardMatcher
def initialize(@expected : TestExpression(Symbol))
end
def description : String
"received message #{@expected.label}"
end
def match?(actual : TestExpression(T)) : Bool forall T
double = actual.value.as(Double)
calls = double.spectator_stub_calls(@expected.value)
!calls.empty?
end
def failure_message(actual : TestExpression(T)) : String forall T
"#{actual.value} did not receive #{@expected.label}"
end
def values(actual : TestExpression(T)) forall T
{
expected: "1 time with any arguments",
received: "0 times with any arguments"
}
end
end
end

View file

@ -1,12 +1,8 @@
module Spectator module Spectator
class MethodCall(T, NT) abstract class MethodCall
getter name : Symbol getter name : Symbol
getter args : T def initialize(@name : Symbol)
getter options : NT
def initialize(@name : Symbol, @args : T, @options : NT)
end end
end end
end end