Cleanup have_ variant by using a new matcher

This commit is contained in:
Michael Miller 2019-06-01 23:27:16 -06:00
parent 16bcce59ae
commit 091cbaa81a
2 changed files with 69 additions and 3 deletions

View file

@ -579,9 +579,11 @@ module Spectator::DSL
{% if call.name.starts_with?("be_") %}
# Remove `be_` prefix.
{% method_name = call.name[3..-1] %}
{% matcher = "PredicateMatcher" %}
{% elsif call.name.starts_with?("have_") %}
# Swap `have_` with `has_`.
{% method_name = ("has_" + call.name[5..-1].stringify).id %}
# Remove `have_` prefix.
{% method_name = call.name[5..-1] %}
{% matcher = "HavePredicateMatcher" %}
{% else %}
{% raise "Undefined local variable or method '#{call}'" %}
{% end %}
@ -598,7 +600,7 @@ module Spectator::DSL
{% end %}
label << ')'
{% end %}
::Spectator::Matchers::PredicateMatcher.new(descriptor, label.to_s)
::Spectator::Matchers::{{matcher.id}}.new(descriptor, label.to_s)
end
end
end

View file

@ -0,0 +1,64 @@
require "./value_matcher"
module Spectator::Matchers
# Matcher that tests one or more "has" predicates
# (methods ending in '?' and starting with 'has_').
# The `ExpectedType` type param should be a `NamedTuple`.
# Each key in the tuple is a predicate (without the '?' and 'has_' prefix) to test.
# Each value is a a `Tuple` of arguments to pass to the predicate method.
struct HavePredicateMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(values)
# Test each predicate and immediately return false if one is false.
{% for attribute in ExpectedType.keys %}
return false unless values[{{attribute.symbolize}}]
{% end %}
# All checks passed if this point is reached.
true
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial) : MatchData
values = snapshot_values(partial.actual)
MatchData.new(match?(values), values, partial.label, label)
end
# Captures all of the actual values.
# A `NamedTuple` is returned,
# with each key being the attribute.
private def snapshot_values(actual)
{% begin %}
{
{% for attribute in ExpectedType.keys %}
{{attribute}}: actual.has_{{attribute}}?(*@expected[{{attribute.symbolize}}]),
{% end %}
}
{% end %}
end
# Match data specific to this matcher.
private struct MatchData(ActualType) < MatchData
# Creates the match data.
def initialize(matched, @named_tuple : ActualType, @actual_label : String, @expected_label : String)
super(matched)
end
# Information about the match.
getter named_tuple
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@actual_label} has #{@expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@actual_label} does not have #{@expected_label}"
end
end
end
end