mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Implement expect_any_instance_of
This commit is contained in:
parent
a15e2a97b1
commit
ac9b3ad1fe
4 changed files with 146 additions and 2 deletions
|
@ -121,6 +121,11 @@ module Spectator::DSL
|
||||||
Mocks::AllowAnyInstance(T).new
|
Mocks::AllowAnyInstance(T).new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
macro expect_any_instance_of(type, _source_file = __FILE__, _source_line = __LINE__)
|
||||||
|
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
|
||||||
|
::Spectator::Mocks::ExpectAnyInstance({{type}}).new(%source)
|
||||||
|
end
|
||||||
|
|
||||||
macro receive(method_name, _source_file = __FILE__, _source_line = __LINE__)
|
macro receive(method_name, _source_file = __FILE__, _source_line = __LINE__)
|
||||||
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
|
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
|
||||||
::Spectator::Mocks::NilMethodStub.new({{method_name.id.symbolize}}, %source)
|
::Spectator::Mocks::NilMethodStub.new({{method_name.id.symbolize}}, %source)
|
||||||
|
|
111
src/spectator/matchers/receive_type_matcher.cr
Normal file
111
src/spectator/matchers/receive_type_matcher.cr
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
require "../mocks"
|
||||||
|
require "./standard_matcher"
|
||||||
|
|
||||||
|
module Spectator::Matchers
|
||||||
|
struct ReceiveTypeMatcher < StandardMatcher
|
||||||
|
alias Range = ::Range(Int32, Int32) | ::Range(Nil, Int32) | ::Range(Int32, Nil)
|
||||||
|
|
||||||
|
def initialize(@expected : TestExpression(Symbol), @args : Mocks::Arguments? = nil, @range : Range? = nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def description : String
|
||||||
|
range = @range
|
||||||
|
"received message #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "At least once"} with #{@args || "any arguments"}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def match?(actual : TestExpression(T)) : Bool forall T
|
||||||
|
calls = Harness.current.mocks.calls_for_type(actual.value, @expected.value)
|
||||||
|
calls.select! { |call| @args === call.args } if @args
|
||||||
|
if (range = @range)
|
||||||
|
range.includes?(calls.size)
|
||||||
|
else
|
||||||
|
!calls.empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def failure_message(actual : TestExpression(T)) : String forall T
|
||||||
|
range = @range
|
||||||
|
"#{actual.label} did not receive #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "at least once"} with #{@args || "any arguments"}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def values(actual : TestExpression(T)) forall T
|
||||||
|
calls = Harness.current.mocks.calls_for_type(T, @expected.value)
|
||||||
|
calls.select! { |call| @args === call.args } if @args
|
||||||
|
range = @range
|
||||||
|
{
|
||||||
|
expected: "#{range ? "#{humanize_range(range)} time(s)" : "At least once"} with #{@args || "any arguments"}",
|
||||||
|
received: "#{calls.size} time(s)",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def with(*args, **opts)
|
||||||
|
args = Mocks::GenericArguments.new(args, opts)
|
||||||
|
ReceiveTypeMatcher.new(@expected, args, @range)
|
||||||
|
end
|
||||||
|
|
||||||
|
def once
|
||||||
|
ReceiveTypeMatcher.new(@expected, @args, (1..1))
|
||||||
|
end
|
||||||
|
|
||||||
|
def twice
|
||||||
|
ReceiveTypeMatcher.new(@expected, @args, (2..2))
|
||||||
|
end
|
||||||
|
|
||||||
|
def exactly(count)
|
||||||
|
Count.new(@expected, (count..count))
|
||||||
|
end
|
||||||
|
|
||||||
|
def at_least(count)
|
||||||
|
Count.new(@expected, (count..))
|
||||||
|
end
|
||||||
|
|
||||||
|
def at_most(count)
|
||||||
|
Count.new(@expected, (..count))
|
||||||
|
end
|
||||||
|
|
||||||
|
def at_least_once
|
||||||
|
at_least(1).times
|
||||||
|
end
|
||||||
|
|
||||||
|
def at_least_twice
|
||||||
|
at_least(2).times
|
||||||
|
end
|
||||||
|
|
||||||
|
def at_most_once
|
||||||
|
at_most(1).times
|
||||||
|
end
|
||||||
|
|
||||||
|
def at_most_twice
|
||||||
|
at_most(2).times
|
||||||
|
end
|
||||||
|
|
||||||
|
def humanize_range(range : Range)
|
||||||
|
if (min = range.begin)
|
||||||
|
if (max = range.end)
|
||||||
|
if min == max
|
||||||
|
min
|
||||||
|
else
|
||||||
|
"#{min} to #{max}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
"At least #{min}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if (max = range.end)
|
||||||
|
"At most #{max}"
|
||||||
|
else
|
||||||
|
raise "Unexpected endless range"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private struct Count
|
||||||
|
def initialize(@expected : TestExpression(Symbol), @args : Mocks::Arguments?, @range : Range)
|
||||||
|
end
|
||||||
|
|
||||||
|
def times
|
||||||
|
ReceiveTypeMatcher.new(@expected, @args, @range)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
src/spectator/mocks/expect_any_instance.cr
Normal file
23
src/spectator/mocks/expect_any_instance.cr
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
require "./registry"
|
||||||
|
|
||||||
|
module Spectator::Mocks
|
||||||
|
struct ExpectAnyInstance(T)
|
||||||
|
def initialize(@source : Source)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to(stub : MethodStub) : Nil
|
||||||
|
actual = TestValue.new(T)
|
||||||
|
Harness.current.mocks.expect(T, stub.name)
|
||||||
|
value = TestValue.new(stub.name, stub.to_s)
|
||||||
|
matcher = Matchers::ReceiveTypeMatcher.new(value, stub.arguments?)
|
||||||
|
partial = Expectations::ExpectationPartial.new(actual, @source)
|
||||||
|
partial.to_eventually(matcher)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to(stubs : Enumerable(MethodStub)) : Nil
|
||||||
|
stubs.each do |stub|
|
||||||
|
to(stub)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -61,18 +61,23 @@ module Spectator::Mocks
|
||||||
fetch_instance(object).calls.select { |call| call.name == method_name }
|
fetch_instance(object).calls.select { |call| call.name == method_name }
|
||||||
end
|
end
|
||||||
|
|
||||||
def calls_for_type(type, method_name : Symbol)
|
def calls_for_type(type : T.class, method_name : Symbol) forall T
|
||||||
fetch_type(type).calls.select { |call| call.name == method_name }
|
fetch_type(type).calls.select { |call| call.name == method_name }
|
||||||
end
|
end
|
||||||
|
|
||||||
def expected?(object, method_name : Symbol) : Bool
|
def expected?(object, method_name : Symbol) : Bool
|
||||||
fetch_instance(object).expected.includes?(method_name)
|
fetch_instance(object).expected.includes?(method_name) ||
|
||||||
|
fetch_type(object.class).expected.includes?(method_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def expect(object, method_name : Symbol) : Nil
|
def expect(object, method_name : Symbol) : Nil
|
||||||
fetch_instance(object).expected.add(method_name)
|
fetch_instance(object).expected.add(method_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def expect(type : T.class, method_name : Symbol) : Nil forall T
|
||||||
|
fetch_type(type).expected.add(method_name)
|
||||||
|
end
|
||||||
|
|
||||||
private def fetch_instance(object)
|
private def fetch_instance(object)
|
||||||
key = unique_key(object)
|
key = unique_key(object)
|
||||||
if @entries.has_key?(key)
|
if @entries.has_key?(key)
|
||||||
|
|
Loading…
Reference in a new issue