From 6e62ccdfc5462c03aabc77c2130c7e47ae39c7e0 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Wed, 6 Mar 2019 11:19:57 -0700 Subject: [PATCH] Update TypeMatcher to use MatchData --- spec/matchers/type_matcher_spec.cr | 173 +++++++++++++++---------- src/spectator/matchers/type_matcher.cr | 44 +++++-- 2 files changed, 134 insertions(+), 83 deletions(-) diff --git a/spec/matchers/type_matcher_spec.cr b/spec/matchers/type_matcher_spec.cr index 5f073a8..070e22d 100644 --- a/spec/matchers/type_matcher_spec.cr +++ b/spec/matchers/type_matcher_spec.cr @@ -1,82 +1,117 @@ require "../spec_helper" describe Spectator::Matchers::TypeMatcher do - describe "#match?" do - context "with the same type" do - it "is true" do - value = "foobar" - partial = new_partial(value) - matcher = Spectator::Matchers::TypeMatcher(String).new - matcher.match?(partial).should be_true + describe "#match" do + context "returned MatchData" do + describe "#matched?" do + context "with the same type" do + it "is true" do + value = "foobar" + partial = new_partial(value) + matcher = Spectator::Matchers::TypeMatcher(String).new + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + end + + context "with a different type" do + it "is false" do + value = "foobar" + partial = new_partial(value) + matcher = Spectator::Matchers::TypeMatcher(Int32).new + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + end + + context "with a parent type" do + it "is true" do + value = IO::Memory.new + partial = new_partial(value) + matcher = Spectator::Matchers::TypeMatcher(IO).new + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + end + + context "with a child type" do + it "is false" do + value = Exception.new("foobar") + partial = new_partial(value) + matcher = Spectator::Matchers::TypeMatcher(ArgumentError).new + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + end + + context "with a mix-in" do + it "is true" do + value = %i[a b c] + partial = new_partial(value) + matcher = Spectator::Matchers::TypeMatcher(Enumerable(Symbol)).new + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + end end - end - context "with a different type" do - it "is false" do - value = "foobar" - partial = new_partial(value) - matcher = Spectator::Matchers::TypeMatcher(Int32).new - matcher.match?(partial).should be_false + describe "#values" do + context "expected" do + it "is the expected type name" do + value = %i[a b c] + partial = new_partial(value) + matcher = Spectator::Matchers::TypeMatcher(String).new + match_data = matcher.match(partial) + match_data.values[:expected].should eq(String) + end + end + + context "actual" do + it "is the actual type name" do + value = %i[a b c] + partial = new_partial(value) + matcher = Spectator::Matchers::TypeMatcher(String).new + match_data = matcher.match(partial) + match_data.values[:actual].should eq(typeof(value)) + end + end end - end - context "with a parent type" do - it "is true" do - value = IO::Memory.new - partial = new_partial(value) - matcher = Spectator::Matchers::TypeMatcher(IO).new - matcher.match?(partial).should be_true + describe "#message" do + it "contains the actual label" do + value = 42 + label = "everything" + partial = new_partial(value, label) + matcher = Spectator::Matchers::TypeMatcher(String).new + match_data = matcher.match(partial) + match_data.message.should contain(label) + end + + it "contains the expected type" do + partial = new_partial(42) + matcher = Spectator::Matchers::TypeMatcher(String).new + match_data = matcher.match(partial) + match_data.message.should contain("String") + end end - end - context "with a child type" do - it "is false" do - value = Exception.new("foobar") - partial = new_partial(value) - matcher = Spectator::Matchers::TypeMatcher(ArgumentError).new - matcher.match?(partial).should be_false + describe "#negated_message" do + it "contains the actual label" do + value = 42 + label = "everything" + partial = new_partial(value, label) + matcher = Spectator::Matchers::TypeMatcher(String).new + match_data = matcher.match(partial) + match_data.negated_message.should contain(label) + end + + it "contains the expected type" do + partial = new_partial(42) + matcher = Spectator::Matchers::TypeMatcher(String).new + match_data = matcher.match(partial) + match_data.negated_message.should contain("String") + end end end - - context "with a mix-in" do - it "is true" do - value = %i[a b c] - partial = new_partial(value) - matcher = Spectator::Matchers::TypeMatcher(Enumerable(Symbol)).new - matcher.match?(partial).should be_true - end - end - end - - describe "#message" do - it "contains the actual label" do - value = 42 - label = "everything" - partial = new_partial(value, label) - matcher = Spectator::Matchers::TypeMatcher(String).new - matcher.message(partial).should contain(label) - end - - it "contains the expected type" do - partial = new_partial(42) - matcher = Spectator::Matchers::TypeMatcher(String).new - matcher.message(partial).should contain("String") - end - end - - describe "#negated_message" do - it "contains the actual label" do - value = 42 - label = "everything" - partial = new_partial(value, label) - matcher = Spectator::Matchers::TypeMatcher(String).new - matcher.negated_message(partial).should contain(label) - end - - it "contains the expected type" do - partial = new_partial(42) - matcher = Spectator::Matchers::TypeMatcher(String).new - matcher.negated_message(partial).should contain("String") - end end end diff --git a/src/spectator/matchers/type_matcher.cr b/src/spectator/matchers/type_matcher.cr index b670369..3d79970 100644 --- a/src/spectator/matchers/type_matcher.cr +++ b/src/spectator/matchers/type_matcher.cr @@ -11,27 +11,43 @@ module Spectator::Matchers end # Determines whether the matcher is satisfied with the value given to it. - # True is returned if the match was successful, false otherwise. - def match?(partial) - partial.actual.is_a?(Expected) + private def match?(actual) + actual.is_a?(Expected) 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 - raise NotImplementedError.new("#match") + def match(partial) + actual = partial.actual + MatchData(Expected, typeof(actual)).new(match?(actual), partial.label) end - # Describes the condition that satisfies the matcher. - # This is informational and displayed to the end-user. - def message(partial) - "Expected #{partial.label} to be a #{label}" - end + # Match data specific to this matcher. + private struct MatchData(ExpectedType, ActualType) < MatchData + # Creates the match data. + def initialize(matched, @actual_label : String) + super(matched) + end - # Describes the condition that won't satsify the matcher. - # This is informational and displayed to the end-user. - def negated_message(partial) - "Expected #{partial.label} to not be a #{label}" + # Information about the match. + def values + { + expected: ExpectedType, + actual: ActualType, + } + end + + # Describes the condition that satisfies the matcher. + # This is informational and displayed to the end-user. + def message + "#{@actual_label} is a #{ExpectedType}" + end + + # Describes the condition that won't satsify the matcher. + # This is informational and displayed to the end-user. + def negated_message + "#{@actual_label} is not a #{ExpectedType}" + end end end end