Add count to ReceiveMatcher

This commit is contained in:
Michael Miller 2022-07-13 16:48:38 -06:00
parent be973cdc4b
commit e29c732d0f
No known key found for this signature in database
GPG Key ID: 32B47AE8F388A1FF
1 changed files with 26 additions and 8 deletions

View File

@ -6,8 +6,10 @@ require "./matcher"
module Spectator::Matchers
# Matcher that inspects stubbable objects for method calls.
struct ReceiveMatcher < Matcher
alias Count = Range(Int32?, Int32?)
# Creates the matcher for expecting a method call matching a stub.
def initialize(@stub : Stub)
def initialize(@stub : Stub, @count : Count = Count.new(1, nil))
end
# Creates the matcher for expecting a method call with any arguments.
@ -20,21 +22,23 @@ module Spectator::Matchers
# Returns a new matcher with an argument constraint.
def with(*args, **kwargs) : self
stub = @stub.with(*args, **kwargs)
self.class.new(stub)
self.class.new(stub, @count)
end
# Short text about the matcher's purpose.
def description : String
"received #{@stub}"
"received #{@stub} #{humanize_count}"
end
# Actually performs the test against the expression (value or block).
def match(actual : Expression(Stubbable) | Expression(StubbedType)) : MatchData
stubbed = actual.value
if stubbed._spectator_calls.any? { |call| @stub === call }
SuccessfulMatchData.new("#{actual.label} received #{@stub}")
calls = relevant_calls(stubbed)
if @count.includes?(calls.size)
SuccessfulMatchData.new("#{actual.label} received #{@stub} #{humanize_count}")
else
FailedMatchData.new("#{actual.label} received #{@stub}", "#{actual.label} did not receive #{@stub}", values(actual).to_a)
FailedMatchData.new("#{actual.label} received #{@stub} #{humanize_count}",
"#{actual.label} did not receive #{@stub}", values(actual).to_a)
end
end
@ -46,10 +50,11 @@ module Spectator::Matchers
# Performs the test against the expression (value or block), but inverted.
def negated_match(actual : Expression(Stubbable) | Expression(StubbedType)) : MatchData
stubbed = actual.value
if stubbed._spectator_calls.any? { |call| @stub === call }
calls = relevant_calls(stubbed)
if @count.includes?(calls.size)
FailedMatchData.new("#{actual.label} did not receive #{@stub}", "#{actual.label} received #{@stub}", negated_values(actual).to_a)
else
SuccessfulMatchData.new("#{actual.label} did not receive #{@stub}")
SuccessfulMatchData.new("#{actual.label} did not receive #{@stub} #{humanize_count}")
end
end
@ -74,6 +79,19 @@ module Spectator::Matchers
}
end
# Filtered list of method calls relevant to the matcher.
private def relevant_calls(stubbable)
stubbable._spectator_calls.select { |call| @stub === call }
end
private def humanize_count
case {@count.begin, @count.end}
when {Int32, nil} then "at least #{@count.begin} times"
when {nil, Int32} then "at most #{@count.end} times"
else "any number of times"
end
end
# Formatted list of method calls.
private def method_call_list(stubbable)
calls = stubbable._spectator_calls