From 03754321b5a432a4a784742a6ec25a3c1b1151df Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Sat, 28 May 2022 09:18:49 -0600 Subject: [PATCH] Fix existing mock DSL macros Initial code for mock DSL. --- spec/spectator/dsl/mocks/mock_spec.cr | 38 +++++++++++++++++++++++++++ src/spectator/dsl/mocks.cr | 17 +++++++++--- 2 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 spec/spectator/dsl/mocks/mock_spec.cr diff --git a/spec/spectator/dsl/mocks/mock_spec.cr b/spec/spectator/dsl/mocks/mock_spec.cr new file mode 100644 index 0000000..f32c61d --- /dev/null +++ b/spec/spectator/dsl/mocks/mock_spec.cr @@ -0,0 +1,38 @@ +require "../../../spec_helper" + +Spectator.describe "Mock DSL", :smoke do + context "with a concrete class" do + class ConcreteClass + def method1 + "original" + end + + def method2 : Symbol + :original + end + end + + context "specifying methods as keyword args" do + mock(ConcreteClass, method1: "stubbed", method2: :stubbed) + subject(fake) { mock(ConcreteClass) } + + it "defines a mock with methods" do + aggregate_failures do + expect(fake.method1).to eq("stubbed") + expect(fake.method2).to eq(:stubbed) + end + end + + it "defines a subclass" do + expect(fake).to be_a(ConcreteClass) + end + + it "compiles types without unions" do + aggregate_failures do + expect(fake.method1).to compile_as(String) + expect(fake.method2).to compile_as(Symbol) + end + end + end + end +end diff --git a/src/spectator/dsl/mocks.cr b/src/spectator/dsl/mocks.cr index 4f168e9..545213d 100644 --- a/src/spectator/dsl/mocks.cr +++ b/src/spectator/dsl/mocks.cr @@ -128,16 +128,25 @@ module Spectator::DSL ::Spectator::LazyDouble.new({{**value_methods}}) end - private macro def_mock(type, **value_methods, &block) + private macro def_mock(type, name = nil, **value_methods, &block) {% # Construct a unique type name for the mock by using the number of defined types. index = ::Spectator::DSL::Mocks::TYPES.size mock_type_name = "Mock#{index}".id # Store information about how the mock is defined and its context. # This is important for constructing an instance of the mock later. - ::Spectator::DSL::Mocks::TYPES << {type.id.symbolize, @type.name(generic_args: false).symbolize, mock_type_name.symbolize} %} + ::Spectator::DSL::Mocks::TYPES << {type.id.symbolize, @type.name(generic_args: false).symbolize, mock_type_name.symbolize} - ::Spectator::Mock.define({{type.id}}, {{**value_methods}}){% if block %} do + resolved = type.resolve + base = if resolved.class? + :class + elsif resolved.struct? + :struct + else + :module + end %} + + ::Spectator::Mock.define_subtype({{base}}, {{type.id}}, {{mock_type_name}}, {{name}}, {{**value_methods}}){% if block %} do {% block.body %} end{% end %} end @@ -177,7 +186,7 @@ module Spectator::DSL macro mock(type, **value_methods, &block) {% begin %} - {% if @def %}new_mock{% else %}def_mock{% end %}({{name}}, {{**value_methods}}){% if block %} do + {% if @def %}new_mock{% else %}def_mock{% end %}({{type}}, {{**value_methods}}){% if block %} do {{block.body}} end{% end %} {% end %}