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}}
include ::Spectator::Mocks::Stubs
@spectator_stubs = Deque(::Spectator::Mocks::MethodStub).new
@spectator_stub_calls = Deque(::Spectator::Mocks::MethodCall).new
{{block.body}}
end
{% end %}
{% debug %}
end
def allow(double : ::Spectator::Mocks::Double)

View File

@ -1,4 +1,4 @@
module Spectator
module Spectator::Mocks
module Stubs
private macro stub(definition, &block)
{%
@ -7,9 +7,15 @@ module Spectator
args = nil
body = nil
if definition.is_a?(Call) # stub foo { :bar }
named = false
name = definition.name.id
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
elsif definition.is_a?(TypeDeclaration) # stub foo : Symbol
name = definition.var
@ -20,63 +26,30 @@ module Spectator
raise "Unrecognized stub format"
end
%}
def {{name}}{% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
%method
end
{% if name.ends_with?('=') && name.id != "[]=" %}
def {{name}}(arg)
call = ::Spectator::GenericMethodCall.new({{name.symbolize}}, {arg}, NamedTuple.new)
@spectator_stub_calls << call
stub = @spectator_stubs.find(&.callable?(call))
if stub
stub.as(::Spectator::GenericMethodStub(typeof(%method(arg)))).call(call)
else
%method(arg)
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}})))
else
previous_def({{args.splat}})
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
{% 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
{% debug %}
end
end
end