mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Add respond_to matcher
This commit is contained in:
parent
151108b063
commit
cd60fae157
4 changed files with 213 additions and 1 deletions
|
@ -215,7 +215,7 @@ Items not marked as completed may have partial implementations.
|
||||||
- [ ] Matchers
|
- [ ] Matchers
|
||||||
- [X] Equality matchers - `eq`, `ne`, `be ==`, `be !=`
|
- [X] Equality matchers - `eq`, `ne`, `be ==`, `be !=`
|
||||||
- [X] Comparison matchers - `be <`, `be <=`, `be >`, `be >=`, `be_within[.of]`, `be_close`
|
- [X] Comparison matchers - `be <`, `be <=`, `be >`, `be >=`, `be_within[.of]`, `be_close`
|
||||||
- [X] Type matchers - `be_a`
|
- [X] Type matchers - `be_a`, `respond_to`
|
||||||
- [ ] Collection matchers - `contain`, `have`, `contain_exactly[.in_order|.in_any_order]`, `match_array[.in_order|.in_any_order]`, `start_with`, `end_with`, `be_empty`, `have_key`, `have_value`, `all`, `all_satisfy`
|
- [ ] Collection matchers - `contain`, `have`, `contain_exactly[.in_order|.in_any_order]`, `match_array[.in_order|.in_any_order]`, `start_with`, `end_with`, `be_empty`, `have_key`, `have_value`, `all`, `all_satisfy`
|
||||||
- [X] Truthy matchers - `be`, `be_true`, `be_truthy`, `be_false`, `be_falsey`, `be_nil`
|
- [X] Truthy matchers - `be`, `be_true`, `be_truthy`, `be_false`, `be_falsey`, `be_nil`
|
||||||
- [X] Error matchers - `raise_error`
|
- [X] Error matchers - `raise_error`
|
||||||
|
|
123
spec/matchers/respond_matcher_spec.cr
Normal file
123
spec/matchers/respond_matcher_spec.cr
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
require "../spec_helper"
|
||||||
|
|
||||||
|
describe Spectator::Matchers::RespondMatcher do
|
||||||
|
describe "#match" do
|
||||||
|
context "returned MatchData" do
|
||||||
|
describe "#matched?" do
|
||||||
|
context "one method" do
|
||||||
|
context "with a responding method" do
|
||||||
|
it "is true" do
|
||||||
|
array = %i[a b c]
|
||||||
|
partial = new_partial(array)
|
||||||
|
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil)).new
|
||||||
|
match_data = matcher.match(partial)
|
||||||
|
match_data.matched?.should be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "against a non-responding method" do
|
||||||
|
it "is false" do
|
||||||
|
array = %i[a b c]
|
||||||
|
partial = new_partial(array)
|
||||||
|
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(downcase: Nil)).new
|
||||||
|
match_data = matcher.match(partial)
|
||||||
|
match_data.matched?.should be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "multiple methods" do
|
||||||
|
context "with one responding method" do
|
||||||
|
it "is false" do
|
||||||
|
array = %i[a b c]
|
||||||
|
partial = new_partial(array)
|
||||||
|
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
|
||||||
|
match_data = matcher.match(partial)
|
||||||
|
match_data.matched?.should be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with all responding methods" do
|
||||||
|
it "is true" do
|
||||||
|
array = %i[a b c]
|
||||||
|
partial = new_partial(array)
|
||||||
|
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, to_a: Nil)).new
|
||||||
|
match_data = matcher.match(partial)
|
||||||
|
match_data.matched?.should be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with no responding methods" do
|
||||||
|
it "is false" do
|
||||||
|
array = %i[a b c]
|
||||||
|
partial = new_partial(array)
|
||||||
|
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(downcase: Nil, upcase: Nil)).new
|
||||||
|
match_data = matcher.match(partial)
|
||||||
|
match_data.matched?.should be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#values" do
|
||||||
|
it "contains a key for each expected method" do
|
||||||
|
array = %i[a b c]
|
||||||
|
partial = new_partial(array)
|
||||||
|
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
|
||||||
|
match_data = matcher.match(partial)
|
||||||
|
match_data_has_key?(match_data.values, :"responds to #size").should be_true
|
||||||
|
match_data_has_key?(match_data.values, :"responds to #downcase").should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "has the actual values" do
|
||||||
|
array = %i[a b c]
|
||||||
|
partial = new_partial(array)
|
||||||
|
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
|
||||||
|
match_data = matcher.match(partial)
|
||||||
|
match_data_value_sans_prefix(match_data.values, :"responds to #size")[:value].should be_true
|
||||||
|
match_data_value_sans_prefix(match_data.values, :"responds to #downcase")[:value].should be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#message" do
|
||||||
|
it "contains the actual label" do
|
||||||
|
value = "foobar"
|
||||||
|
label = "everything"
|
||||||
|
partial = new_partial(value, label)
|
||||||
|
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
|
||||||
|
match_data = matcher.match(partial)
|
||||||
|
match_data.message.should contain(label)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "contains the method names" do
|
||||||
|
value = "foobar"
|
||||||
|
partial = new_partial(value)
|
||||||
|
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
|
||||||
|
match_data = matcher.match(partial)
|
||||||
|
match_data.message.should contain("#size")
|
||||||
|
match_data.message.should contain("#downcase")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#negated_message" do
|
||||||
|
it "contains the actual label" do
|
||||||
|
value = "foobar"
|
||||||
|
label = "everything"
|
||||||
|
partial = new_partial(value, label)
|
||||||
|
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
|
||||||
|
match_data = matcher.match(partial)
|
||||||
|
match_data.negated_message.should contain(label)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "contains the method names" do
|
||||||
|
value = "foobar"
|
||||||
|
partial = new_partial(value)
|
||||||
|
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
|
||||||
|
match_data = matcher.match(partial)
|
||||||
|
match_data.message.should contain("#size")
|
||||||
|
match_data.negated_message.should contain("#downcase")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -90,6 +90,22 @@ module Spectator::DSL
|
||||||
be_a({{expected}})
|
be_a({{expected}})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Indicates that some value should respond to a method call.
|
||||||
|
# One or more method names can be provided.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# ```
|
||||||
|
# expect("foobar").to respond_to(:downcase)
|
||||||
|
# expect(%i[a b c]).to respond_to(:size, :first)
|
||||||
|
# ```
|
||||||
|
macro respond_to(*expected)
|
||||||
|
::Spectator::Matchers::RespondMatcher({% begin %}NamedTuple(
|
||||||
|
{% for method in expected %}
|
||||||
|
{{method.id.stringify}}: Nil,
|
||||||
|
{% end %}
|
||||||
|
){% end %}).new
|
||||||
|
end
|
||||||
|
|
||||||
# Indicates that some value should be less than another.
|
# Indicates that some value should be less than another.
|
||||||
# The < operator is used for this check.
|
# The < operator is used for this check.
|
||||||
# The value passed to this method is the value expected to be larger.
|
# The value passed to this method is the value expected to be larger.
|
||||||
|
|
73
src/spectator/matchers/respond_matcher.cr
Normal file
73
src/spectator/matchers/respond_matcher.cr
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
require "./value_matcher"
|
||||||
|
|
||||||
|
module Spectator::Matchers
|
||||||
|
# Matcher that tests that a type responds to a method call.
|
||||||
|
# The instance is tested with the `responds_to?` method.
|
||||||
|
# The `ExpectedType` type param should be a `NamedTuple`,
|
||||||
|
# with each key being the method to check and the value is ignored.
|
||||||
|
struct RespondMatcher(ExpectedType) < Matcher
|
||||||
|
# Determines whether the matcher is satisfied with the value given to it.
|
||||||
|
private def match?(actual)
|
||||||
|
# The snapshot did the hard work.
|
||||||
|
# Here just check if all values are true.
|
||||||
|
actual.values.all?
|
||||||
|
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)
|
||||||
|
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 method in ExpectedType.keys %}
|
||||||
|
{{method.stringify}}: actual.responds_to?({{method.symbolize}}),
|
||||||
|
{% end %}
|
||||||
|
}
|
||||||
|
{% end %}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Textual representation of what the matcher expects.
|
||||||
|
def label
|
||||||
|
# Prefix every method name with # and join them with commas.
|
||||||
|
{{ExpectedType.keys.map { |e| "##{e}".id }.splat.stringify}}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Match data specific to this matcher.
|
||||||
|
private struct MatchData(ActualType) < MatchData
|
||||||
|
# Creates the match data.
|
||||||
|
def initialize(matched, @actual : ActualType, @actual_label : String, @expected_label : String)
|
||||||
|
super(matched)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Information about the match.
|
||||||
|
def named_tuple
|
||||||
|
{% begin %}
|
||||||
|
{
|
||||||
|
{% for method in ActualType.keys %}
|
||||||
|
{{"responds to #" + method.stringify}}: @actual[{{method.symbolize}}],
|
||||||
|
{% end %}
|
||||||
|
}
|
||||||
|
{% end %}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Describes the condition that satisfies the matcher.
|
||||||
|
# This is informational and displayed to the end-user.
|
||||||
|
def message
|
||||||
|
"#{@actual_label} responds to #{@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 respond to #{@expected_label}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue