mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Fix resolution issue when mocked types use custom types
GitLab issue 51 is affected. https://gitlab.com/arctic-fox/spectator/-/issues/51 Private types cannot be referenced with mocks.
This commit is contained in:
parent
c3e7edc700
commit
4b68b8e3de
4 changed files with 133 additions and 35 deletions
|
@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fix macro logic to support free variables, 'self', and variants on stubbed methods. [#48](https://github.com/icy-arctic-fox/spectator/issues/48)
|
- Fix macro logic to support free variables, 'self', and variants on stubbed methods. [#48](https://github.com/icy-arctic-fox/spectator/issues/48)
|
||||||
- Fix method stubs used on methods that capture blocks.
|
- Fix method stubs used on methods that capture blocks.
|
||||||
|
- Fix type name resolution for when using custom types in a mocked typed.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Simplify string representation of mock-related types.
|
- Simplify string representation of mock-related types.
|
||||||
|
|
|
@ -1,31 +1,33 @@
|
||||||
require "../spec_helper"
|
require "../spec_helper"
|
||||||
|
|
||||||
private class Foo
|
module GitLabIssue51
|
||||||
def call(str : String) : String?
|
class Foo
|
||||||
""
|
def call(str : String) : String?
|
||||||
|
""
|
||||||
|
end
|
||||||
|
|
||||||
|
def alt1_call(str : String) : String?
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def alt2_call(str : String) : String?
|
||||||
|
[str, nil].sample
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def alt1_call(str : String) : String?
|
class Bar
|
||||||
nil
|
def call(a_foo) : Nil # Must add nil restriction here, otherwise a segfault occurs from returning the result of #alt2_call.
|
||||||
end
|
a_foo.call("")
|
||||||
|
a_foo.alt1_call("")
|
||||||
def alt2_call(str : String) : String?
|
a_foo.alt2_call("")
|
||||||
[str, nil].sample
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private class Bar
|
Spectator.describe GitLabIssue51::Bar do
|
||||||
def call(a_foo) : Nil # Must add nil restriction here, otherwise a segfault occurs from returning the result of #alt2_call.
|
mock GitLabIssue51::Foo, call: "", alt1_call: "", alt2_call: ""
|
||||||
a_foo.call("")
|
|
||||||
a_foo.alt1_call("")
|
|
||||||
a_foo.alt2_call("")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Spectator.describe Bar do
|
let(:foo) { mock(GitLabIssue51::Foo) }
|
||||||
mock Foo, call: "", alt1_call: "", alt2_call: ""
|
|
||||||
|
|
||||||
let(:foo) { mock(Foo) }
|
|
||||||
subject(:call) { described_class.new.call(foo) }
|
subject(:call) { described_class.new.call(foo) }
|
||||||
|
|
||||||
describe "#call" do
|
describe "#call" do
|
||||||
|
|
|
@ -29,8 +29,18 @@ Spectator.describe Spectator::Mock do
|
||||||
@_spectator_invocations << :method3
|
@_spectator_invocations << :method3
|
||||||
"original"
|
"original"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def method4 : Thing
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def method5 : OtherThing
|
||||||
|
OtherThing.new
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class OtherThing; end
|
||||||
|
|
||||||
Spectator::Mock.define_subtype(:class, Thing, MockThing, :mock_name, method1: 123) do
|
Spectator::Mock.define_subtype(:class, Thing, MockThing, :mock_name, method1: 123) do
|
||||||
stub def method2
|
stub def method2
|
||||||
:stubbed
|
:stubbed
|
||||||
|
@ -104,6 +114,20 @@ Spectator.describe Spectator::Mock do
|
||||||
mock.method3
|
mock.method3
|
||||||
expect(mock._spectator_invocations).to contain_exactly(:method3)
|
expect(mock._spectator_invocations).to contain_exactly(:method3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "can reference its own type" do
|
||||||
|
new_mock = MockThing.new
|
||||||
|
stub = Spectator::ValueStub.new(:method4, new_mock)
|
||||||
|
mock._spectator_define_stub(stub)
|
||||||
|
expect(mock.method4).to be(new_mock)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can reference other types in the original namespace" do
|
||||||
|
other = OtherThing.new
|
||||||
|
stub = Spectator::ValueStub.new(:method5, other)
|
||||||
|
mock._spectator_define_stub(stub)
|
||||||
|
expect(mock.method5).to be(other)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with an abstract class" do
|
context "with an abstract class" do
|
||||||
|
@ -120,8 +144,14 @@ Spectator.describe Spectator::Mock do
|
||||||
end
|
end
|
||||||
|
|
||||||
abstract def method4
|
abstract def method4
|
||||||
|
|
||||||
|
abstract def method4 : Thing
|
||||||
|
|
||||||
|
abstract def method5 : OtherThing
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class OtherThing; end
|
||||||
|
|
||||||
Spectator::Mock.define_subtype(:class, Thing, MockThing, :mock_name, method2: :stubbed) do
|
Spectator::Mock.define_subtype(:class, Thing, MockThing, :mock_name, method2: :stubbed) do
|
||||||
stub def method1 : Int32 # NOTE: Return type is required since one wasn't provided in the parent.
|
stub def method1 : Int32 # NOTE: Return type is required since one wasn't provided in the parent.
|
||||||
123
|
123
|
||||||
|
@ -199,6 +229,20 @@ Spectator.describe Spectator::Mock do
|
||||||
mock.method3
|
mock.method3
|
||||||
expect(mock._spectator_invocations).to contain_exactly(:method3)
|
expect(mock._spectator_invocations).to contain_exactly(:method3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "can reference its own type" do
|
||||||
|
new_mock = MockThing.new
|
||||||
|
stub = Spectator::ValueStub.new(:method4, new_mock)
|
||||||
|
mock._spectator_define_stub(stub)
|
||||||
|
expect(mock.method4).to be(new_mock)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can reference other types in the original namespace" do
|
||||||
|
other = OtherThing.new
|
||||||
|
stub = Spectator::ValueStub.new(:method5, other)
|
||||||
|
mock._spectator_define_stub(stub)
|
||||||
|
expect(mock.method5).to be(other)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with an abstract struct" do
|
context "with an abstract struct" do
|
||||||
|
@ -215,8 +259,14 @@ Spectator.describe Spectator::Mock do
|
||||||
end
|
end
|
||||||
|
|
||||||
abstract def method4
|
abstract def method4
|
||||||
|
|
||||||
|
abstract def method4 : Thing
|
||||||
|
|
||||||
|
abstract def method5 : OtherThing
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class OtherThing; end
|
||||||
|
|
||||||
Spectator::Mock.define_subtype(:struct, Thing, MockThing, :mock_name, method2: :stubbed) do
|
Spectator::Mock.define_subtype(:struct, Thing, MockThing, :mock_name, method2: :stubbed) do
|
||||||
stub def method1 : Int32 # NOTE: Return type is required since one wasn't provided in the parent.
|
stub def method1 : Int32 # NOTE: Return type is required since one wasn't provided in the parent.
|
||||||
123
|
123
|
||||||
|
@ -286,6 +336,22 @@ Spectator.describe Spectator::Mock do
|
||||||
mock.method3
|
mock.method3
|
||||||
expect(mock._spectator_invocations).to contain_exactly(:method3)
|
expect(mock._spectator_invocations).to contain_exactly(:method3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "can reference its own type" do
|
||||||
|
mock = self.mock # FIXME: Workaround for passing by value messing with stubs.
|
||||||
|
new_mock = MockThing.new
|
||||||
|
stub = Spectator::ValueStub.new(:method4, new_mock)
|
||||||
|
mock._spectator_define_stub(stub)
|
||||||
|
expect(mock.method4).to be_a(Thing)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can reference other types in the original namespace" do
|
||||||
|
mock = self.mock # FIXME: Workaround for passing by value messing with stubs.
|
||||||
|
other = OtherThing.new
|
||||||
|
stub = Spectator::ValueStub.new(:method5, other)
|
||||||
|
mock._spectator_define_stub(stub)
|
||||||
|
expect(mock.method5).to be(other)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "class method stubs" do
|
context "class method stubs" do
|
||||||
|
@ -301,8 +367,18 @@ Spectator.describe Spectator::Mock do
|
||||||
def self.baz(arg)
|
def self.baz(arg)
|
||||||
yield
|
yield
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.thing : Thing
|
||||||
|
new
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.other : OtherThing
|
||||||
|
OtherThing.new
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class OtherThing; end
|
||||||
|
|
||||||
Spectator::Mock.define_subtype(:class, Thing, MockThing) do
|
Spectator::Mock.define_subtype(:class, Thing, MockThing) do
|
||||||
stub def self.foo
|
stub def self.foo
|
||||||
:stub
|
:stub
|
||||||
|
@ -367,6 +443,20 @@ Spectator.describe Spectator::Mock do
|
||||||
expect(restricted(mock)).to eq(:stub)
|
expect(restricted(mock)).to eq(:stub)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "can reference its own type" do
|
||||||
|
new_mock = MockThing.new
|
||||||
|
stub = Spectator::ValueStub.new(:thing, new_mock)
|
||||||
|
mock._spectator_define_stub(stub)
|
||||||
|
expect(mock.thing).to be(new_mock)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can reference other types in the original namespace" do
|
||||||
|
other = OtherThing.new
|
||||||
|
stub = Spectator::ValueStub.new(:other, other)
|
||||||
|
mock._spectator_define_stub(stub)
|
||||||
|
expect(mock.other).to be(other)
|
||||||
|
end
|
||||||
|
|
||||||
describe "._spectator_clear_stubs" do
|
describe "._spectator_clear_stubs" do
|
||||||
before { mock._spectator_define_stub(foo_stub) }
|
before { mock._spectator_define_stub(foo_stub) }
|
||||||
|
|
||||||
|
|
|
@ -218,24 +218,29 @@ module Spectator::DSL
|
||||||
# end
|
# end
|
||||||
# ```
|
# ```
|
||||||
private macro def_mock(type, name = nil, **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.
|
{% resolved = type.resolve
|
||||||
index = ::Spectator::DSL::Mocks::TYPES.size
|
# Construct a unique type name for the mock by using the number of defined types.
|
||||||
mock_type_name = "Mock#{index}".id
|
index = ::Spectator::DSL::Mocks::TYPES.size
|
||||||
|
# The type is nested under the original so that any type names from the original can be resolved.
|
||||||
|
mock_type_name = "Mock#{index}".id
|
||||||
|
|
||||||
# Store information about how the mock is defined and its context.
|
# Store information about how the mock is defined and its context.
|
||||||
# This is important for constructing an instance of the mock later.
|
# 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, "::#{resolved.name}::#{mock_type_name}".id.symbolize}
|
||||||
|
|
||||||
resolved = type.resolve
|
base = if resolved.class?
|
||||||
base = if resolved.class?
|
:class
|
||||||
:class
|
elsif resolved.struct?
|
||||||
elsif resolved.struct?
|
:struct
|
||||||
:struct
|
else
|
||||||
else
|
:module
|
||||||
:module
|
end %}
|
||||||
end %}
|
|
||||||
|
|
||||||
::Spectator::Mock.define_subtype({{base}}, {{type.id}}, {{mock_type_name}}, {{name}}, {{**value_methods}}) {{block}}
|
{% begin %}
|
||||||
|
{{base.id}} ::{{resolved.name}}
|
||||||
|
::Spectator::Mock.define_subtype({{base}}, {{type.id}}, {{mock_type_name}}, {{name}}, {{**value_methods}}) {{block}}
|
||||||
|
end
|
||||||
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Instantiates a mock.
|
# Instantiates a mock.
|
||||||
|
|
Loading…
Reference in a new issue