Started playing around with method stubs

This commit is contained in:
Michael Miller 2019-10-12 16:30:46 -06:00
parent 4b57ddab80
commit 0b6465e6bc
5 changed files with 80 additions and 3 deletions

View file

@ -1,26 +1,50 @@
require "./method_stub"
module Spectator module Spectator
abstract class Double abstract class Double
@stubs = Deque(MethodStub).new
private macro delegate_internal(method, *args)
# Modified version of Object#delegate
{% if method.id.ends_with?('=') && method.id != "[]=" %}
@internal.{{method.id}} {{args.splat}}
{% else %}
@internal.{{method.id}}({{args.splat}})
{% end %}
end
macro stub(definition, &block) macro stub(definition, &block)
{% {%
name = nil name = nil
params = nil
args = nil args = nil
body = nil body = nil
if definition.is_a?(Call) # stub foo { :bar } if definition.is_a?(Call) # stub foo { :bar }
name = definition.name.id name = definition.name.id
args = definition.args params = definition.args
args = params.map { |p| p.is_a?(TypeDeclaration) ? p.var : p.id }
body = definition.block.is_a?(Nop) ? block : definition.block body = definition.block.is_a?(Nop) ? block : definition.block
elsif definition.is_a?(TypeDeclaration) # stub foo : Symbol elsif definition.is_a?(TypeDeclaration) # stub foo : Symbol
name = definition.var name = definition.var
params = [] of MacroId
args = [] of MacroId args = [] of MacroId
body = block body = block
else else
raise "Unrecognized stub format" raise "Unrecognized stub format"
end end
%} %}
delegate {{name}}, to: @internal
def {{name}}({{params.splat}})
%stub = @stubs.find(&.callable?({{name.symbolize}}{% unless args.empty? %}, {{args.splat}}{% end %}))
if %stub
%stub.call({{args.splat}})
else
delegate_internal({{name}}{% unless args.empty? %}, {{args.splat}}{% end %})
end
end
private class Internal private class Internal
def {{name}}({{args.splat}}) def {{name}}({{params.splat}})
{% if body && !body.is_a?(Nop) %} {% if body && !body.is_a?(Nop) %}
{{body.body}} {{body.body}}
{% else %} {% else %}
@ -29,5 +53,9 @@ module Spectator
end end
end end
end end
protected def spectator_define_stub(stub : MethodStub) : Nil
@stubs << stub
end
end end
end end

View file

@ -1,4 +1,6 @@
require "../double" require "../double"
require "../generic_method_stub"
require "../open_mock"
module Spectator::DSL module Spectator::DSL
macro double(name, &block) macro double(name, &block)
@ -21,4 +23,13 @@ module Spectator::DSL
end end
{% end %} {% end %}
end end
def allow(double : ::Spectator::Double)
OpenMock.new(double)
end
macro receive(method_name, _source_file = __FILE__, _source_line = __LINE__)
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
::Spectator::GenericMethodStub(Nil).new({{method_name.symbolize}}, %source)
end
end end

View file

@ -0,0 +1,13 @@
require "./method_stub"
require "./source"
module Spectator
class GenericMethodStub(ReturnType, *ArgumentTypes) < MethodStub
def callable?(name : Symbol, *args) : Bool
super
end
def call(*args)
end
end
end

View file

@ -0,0 +1,15 @@
require "./source"
module Spectator
abstract class MethodStub
def initialize(@name : Symbol, @source : Source)
end
def callable?(name : Symbol, *args) : Bool
name == @name
end
def call(*args)
end
end
end

View file

@ -0,0 +1,10 @@
module Spectator
struct OpenMock
def initialize(@mock : Double)
end
def to(stub : MethodStub) : Nil
@mock.spectator_define_stub(stub)
end
end
end