Some updates to mocks

Not working correctly for some cases, specifically String.
The default stub can seriously mess up Crystal internals.
It looks like the stubs and spy information will need to be kept outside 
the instance.
This commit is contained in:
Michael Miller 2019-11-03 15:07:25 -07:00
parent db09274746
commit cf8e028bd9
2 changed files with 32 additions and 57 deletions

View file

@ -34,10 +34,12 @@ module Spectator::DSL
{{type.id}} ::{{resolved.id}} {{type.id}} ::{{resolved.id}}
include ::Spectator::Mocks::Stubs include ::Spectator::Mocks::Stubs
@spectator_stubs = Deque(::Spectator::Mocks::MethodStub).new
@spectator_stub_calls = Deque(::Spectator::Mocks::MethodCall).new
{{block.body}} {{block.body}}
end end
{% end %} {% end %}
{% debug %}
end end
def allow(double : ::Spectator::Mocks::Double) def allow(double : ::Spectator::Mocks::Double)

View file

@ -1,4 +1,4 @@
module Spectator module Spectator::Mocks
module Stubs module Stubs
private macro stub(definition, &block) private macro stub(definition, &block)
{% {%
@ -7,9 +7,15 @@ module Spectator
args = nil args = nil
body = nil body = nil
if definition.is_a?(Call) # stub foo { :bar } if definition.is_a?(Call) # stub foo { :bar }
named = false
name = definition.name.id name = definition.name.id
params = definition.args params = definition.args
args = params.map { |p| p.is_a?(TypeDeclaration) ? p.var : p.id } args = params.map do |p|
n = p.is_a?(TypeDeclaration) ? p.var : p.id
r = named ? "#{n}: #{n}".id : n
named = true if n.starts_with?('*')
r
end
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
@ -20,63 +26,30 @@ module Spectator
raise "Unrecognized stub format" raise "Unrecognized stub format"
end end
%} %}
def {{name}}{% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
%method
end
{% if name.ends_with?('=') && name.id != "[]=" %} def {{name}}({{params.splat}}){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
def {{name}}(arg) %args = ::Spectator::Mocks::GenericArguments.create({{args.splat}})
call = ::Spectator::GenericMethodCall.new({{name.symbolize}}, {arg}, NamedTuple.new) %call = ::Spectator::Mocks::GenericMethodCall.new({{name.symbolize}}, %args)
@spectator_stub_calls << call @spectator_stub_calls << %call
stub = @spectator_stubs.find(&.callable?(call)) if (%stub = @spectator_stubs.find(&.callable?(%call)))
if stub %stub.call(%args, typeof(previous_def({{args.splat}})))
stub.as(::Spectator::GenericMethodStub(typeof(%method(arg)))).call(call) else
else previous_def({{args.splat}})
%method(arg) end
end end
def {{name}}({{params.splat}}){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
%args = ::Spectator::Mocks::GenericArguments.create({{args.splat}})
%call = ::Spectator::Mocks::GenericMethodCall.new({{name.symbolize}}, %args)
@spectator_stub_calls << %call
if (%stub = @spectator_stubs.find(&.callable?(%call)))
%stub.call(%args, typeof(previous_def({{args.splat}}) { |*%ya| yield *%ya }))
else
previous_def({{args.splat}}) do |*%yield_args|
yield *%yield_args
end
end end
{% else %}
def {{name}}(*args, **options){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
call = ::Spectator::GenericMethodCall.new({{name.symbolize}}, args, options)
@spectator_stub_calls << call
stub = @spectator_stubs.find(&.callable?(call))
if stub
stub.as(::Spectator::GenericMethodStub(typeof(%method(*args, **options)))).call(call)
else
%method(*args, **options)
end
end
{% if name != "[]=" %}
def {{name}}(*args, **options){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
call = ::Spectator::GenericMethodCall.new({{name.symbolize}}, args, options)
@spectator_stub_calls << call
stub = @spectator_stubs.find(&.callable?(call))
if stub
stub.as(::Spectator::GenericMethodStub(typeof(%method(*args, **options) { |*yield_args| yield *yield_args }))).call(call)
else
%method(*args, **options) do |*yield_args|
yield *yield_args
end
end
end
{% end %}
{% end %}
def %method({{params.splat}}){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
{% if body && !body.is_a?(Nop) %}
{{body.body}}
{% else %}
raise "Stubbed method called without being allowed"
# This code shouldn't be reached, but makes the compiler happy to have a matching type.
{% if definition.is_a?(TypeDeclaration) %}
%x = uninitialized {{definition.type}}
{% else %}
nil
{% end %}
{% end %}
end end
{% debug %}
end end
end end
end end