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
%method(arg) previous_def({{args.splat}})
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
end end
{% if name != "[]=" %} def {{name}}({{params.splat}}){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
def {{name}}(*args, **options){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %} %args = ::Spectator::Mocks::GenericArguments.create({{args.splat}})
call = ::Spectator::GenericMethodCall.new({{name.symbolize}}, args, options) %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}}) { |*%ya| yield *%ya }))
stub.as(::Spectator::GenericMethodStub(typeof(%method(*args, **options) { |*yield_args| yield *yield_args }))).call(call)
else else
%method(*args, **options) do |*yield_args| previous_def({{args.splat}}) do |*%yield_args|
yield *yield_args yield *%yield_args
end end
end 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 end
end end