mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Implement and test DSL for class mocks and doubles
This commit is contained in:
parent
77096b76e9
commit
813983de4b
3 changed files with 178 additions and 1 deletions
|
@ -334,4 +334,52 @@ Spectator.describe "Double DSL", :smoke do
|
||||||
expect { allow(dbl).to receive(:memoize).and_return(override) }.to change { dbl.reference }.from("memoize").to("override")
|
expect { allow(dbl).to receive(:memoize).and_return(override) }.to change { dbl.reference }.from("memoize").to("override")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "class doubles" do
|
||||||
|
double(:class_double) do
|
||||||
|
abstract_stub def self.abstract_method
|
||||||
|
:abstract
|
||||||
|
end
|
||||||
|
|
||||||
|
stub def self.default_method
|
||||||
|
:default
|
||||||
|
end
|
||||||
|
|
||||||
|
stub def self.args(arg)
|
||||||
|
arg
|
||||||
|
end
|
||||||
|
|
||||||
|
stub def self.method1
|
||||||
|
:method1
|
||||||
|
end
|
||||||
|
|
||||||
|
stub def self.reference
|
||||||
|
method1.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(dbl) { class_double(:class_double) }
|
||||||
|
|
||||||
|
it "raises on abstract stubs" do
|
||||||
|
expect { dbl.abstract_method }.to raise_error(Spectator::UnexpectedMessage, /abstract_method/)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can define default stubs" do
|
||||||
|
expect(dbl.default_method).to eq(:default)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can define new stubs" do
|
||||||
|
expect { allow(dbl).to receive(:args).and_return(42) }.to change { dbl.args(5) }.from(5).to(42)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can override class method stubs" do
|
||||||
|
allow(dbl).to receive(:method1).and_return(:override)
|
||||||
|
expect(dbl.method1).to eq(:override)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can reference stubs" do
|
||||||
|
allow(dbl).to receive(:method1).and_return(:reference)
|
||||||
|
expect(dbl.reference).to eq("reference")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -830,4 +830,62 @@ Spectator.describe "Mock DSL", :smoke do
|
||||||
expect { allow(fake).to receive(:memoize).and_return(override) }.to change { fake.reference }.from("memoize").to("override")
|
expect { allow(fake).to receive(:memoize).and_return(override) }.to change { fake.reference }.from("memoize").to("override")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "class mock" do
|
||||||
|
abstract class Dummy
|
||||||
|
def self.abstract_method
|
||||||
|
:not_really_abstract
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.default_method
|
||||||
|
:original
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.args(arg)
|
||||||
|
arg
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.method1
|
||||||
|
:original
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.reference
|
||||||
|
method1.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mock(Dummy) do
|
||||||
|
abstract_stub def self.abstract_method
|
||||||
|
:abstract
|
||||||
|
end
|
||||||
|
|
||||||
|
stub def self.default_method
|
||||||
|
:default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(fake) { class_mock(Dummy) }
|
||||||
|
|
||||||
|
it "raises on abstract stubs" do
|
||||||
|
expect { fake.abstract_method }.to raise_error(Spectator::UnexpectedMessage, /abstract_method/)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can define default stubs" do
|
||||||
|
expect(fake.default_method).to eq(:default)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can define new stubs" do
|
||||||
|
expect { allow(fake).to receive(:args).and_return(42) }.to change { fake.args(5) }.from(5).to(42)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can override class method stubs" do
|
||||||
|
allow(fake).to receive(:method1).and_return(:override)
|
||||||
|
expect(fake.method1).to eq(:override)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can reference stubs" do
|
||||||
|
allow(fake).to receive(:method1).and_return(:reference)
|
||||||
|
expect(fake.reference).to eq("reference")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -101,6 +101,39 @@ module Spectator::DSL
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private macro class_double(name = nil)
|
||||||
|
{% # Find tuples with the same name.
|
||||||
|
found_tuples = ::Spectator::DSL::Mocks::TYPES.select { |tuple| tuple[0] == name.id.symbolize }
|
||||||
|
|
||||||
|
# Split the current context's type namespace into parts.
|
||||||
|
type_parts = @type.name(generic_args: false).split("::")
|
||||||
|
|
||||||
|
# Find tuples in the same context or a parent of where the double was defined.
|
||||||
|
# This is done by comparing each part of their namespaces.
|
||||||
|
found_tuples = found_tuples.select do |tuple|
|
||||||
|
# Split the namespace of the context the double was defined in.
|
||||||
|
context_parts = tuple[1].id.split("::")
|
||||||
|
|
||||||
|
# Compare namespace parts between the context the double was defined in and this context.
|
||||||
|
# This logic below is effectively comparing array elements, but with methods supported by macros.
|
||||||
|
matches = context_parts.map_with_index { |part, i| part == type_parts[i] }
|
||||||
|
matches.all? { |b| b }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sort the results by the number of namespace parts.
|
||||||
|
# The last result will be the double type defined closest to the current context's type.
|
||||||
|
found_tuples = found_tuples.sort_by do |tuple|
|
||||||
|
tuple[1].id.split("::").size
|
||||||
|
end
|
||||||
|
found_tuple = found_tuples.last %}
|
||||||
|
|
||||||
|
{% if found_tuple %}
|
||||||
|
{{found_tuple[2].id}}
|
||||||
|
{% else %}
|
||||||
|
::Spectator::LazyDouble
|
||||||
|
{% end %}
|
||||||
|
end
|
||||||
|
|
||||||
# Defines or instantiates a double.
|
# Defines or instantiates a double.
|
||||||
#
|
#
|
||||||
# When used inside of a method, instantiates a new double.
|
# When used inside of a method, instantiates a new double.
|
||||||
|
@ -198,6 +231,44 @@ module Spectator::DSL
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private macro class_mock(type, **value_methods)
|
||||||
|
{% # Find tuples with the same name.
|
||||||
|
found_tuples = ::Spectator::DSL::Mocks::TYPES.select { |tuple| tuple[0] == type.id.symbolize }
|
||||||
|
|
||||||
|
# Split the current context's type namespace into parts.
|
||||||
|
type_parts = @type.name(generic_args: false).split("::")
|
||||||
|
|
||||||
|
# Find tuples in the same context or a parent of where the mock was defined.
|
||||||
|
# This is done by comparing each part of their namespaces.
|
||||||
|
found_tuples = found_tuples.select do |tuple|
|
||||||
|
# Split the namespace of the context the double was defined in.
|
||||||
|
context_parts = tuple[1].id.split("::")
|
||||||
|
|
||||||
|
# Compare namespace parts between the context the double was defined in and this context.
|
||||||
|
# This logic below is effectively comparing array elements, but with methods supported by macros.
|
||||||
|
matches = context_parts.map_with_index { |part, i| part == type_parts[i] }
|
||||||
|
matches.all? { |b| b }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sort the results by the number of namespace parts.
|
||||||
|
# The last result will be the double type defined closest to the current context's type.
|
||||||
|
found_tuples = found_tuples.sort_by do |tuple|
|
||||||
|
tuple[1].id.split("::").size
|
||||||
|
end
|
||||||
|
found_tuple = found_tuples.last %}
|
||||||
|
|
||||||
|
{% if found_tuple %}
|
||||||
|
{{found_tuple[2].id}}.tap do |mock|
|
||||||
|
{% for key, value in value_methods %}
|
||||||
|
%stub{key} = ::Spectator::ValueStub.new({{key.id.symbolize}}, {{value}})
|
||||||
|
mock._spectator_define_stub(%stub{key})
|
||||||
|
{% end %}
|
||||||
|
end
|
||||||
|
{% else %}
|
||||||
|
{% raise "Type `#{type.id}` must be previously mocked before attempting to instantiate." %}
|
||||||
|
{% end %}
|
||||||
|
end
|
||||||
|
|
||||||
# Targets a stubbable object (such as a mock or double) for operations.
|
# Targets a stubbable object (such as a mock or double) for operations.
|
||||||
#
|
#
|
||||||
# The *stubbable* must be a `Stubbable` or `StubbedType`.
|
# The *stubbable* must be a `Stubbable` or `StubbedType`.
|
||||||
|
|
Loading…
Reference in a new issue