diff --git a/src/spectator/matchers/array_matcher.cr b/src/spectator/matchers/array_matcher.cr index f8178bb..268d0fe 100644 --- a/src/spectator/matchers/array_matcher.cr +++ b/src/spectator/matchers/array_matcher.cr @@ -1,30 +1,22 @@ -require "./value_matcher" +require "../test_value" +require "./failed_match_data" +require "./matcher" +require "./successful_match_data" require "./unordered_array_matcher" module Spectator::Matchers # Matcher for checking that the contents of one array (or similar type) # has the exact same contents as another and in the same order. - struct ArrayMatcher(ExpectedType) < ValueMatcher(Enumerable(ExpectedType)) + struct ArrayMatcher(ExpectedType) < Matcher + private getter expected : TestValue(ExpectedType) + + def initialize(@expected) + end + def description "contains exactly #{expected.label}" end - private def failure_message(actual) - {% raise "This method should never be called" %} - end - - private def failure_message_when_negated(actual) - {% raise "This method should never be called" %} - end - - private def match?(actual) - {% raise "This method should never be called" %} - end - - private def does_not_match?(actual) - {% raise "This method should never be called" %} - end - def match(actual) actual_elements = actual.value.to_a expected_elements = expected.value.to_a @@ -67,7 +59,7 @@ module Spectator::Matchers end end - def failed_size_mismatch(expected_elements, actual_elements, actual_label) + private def failed_size_mismatch(expected_elements, actual_elements, actual_label) FailedMatchData.new("#{actual_label} does not contain exactly #{expected.label} (size mismatch)", [ LabeledValue.new(expected_elements.inspect, "expected"), @@ -77,7 +69,7 @@ module Spectator::Matchers ]) end - def failed_content_mismatch(expected_elements, actual_elements, index, actual_label) + private def failed_content_mismatch(expected_elements, actual_elements, index, actual_label) FailedMatchData.new("#{actual_label} does not contain exactly #{expected.label} (element mismatch)", [ LabeledValue.new(expected_elements[index].inspect, "expected"), @@ -86,7 +78,7 @@ module Spectator::Matchers ]) end - def failed_content_identical(expected_elements, actual_elements, actual_label) + private def failed_content_identical(expected_elements, actual_elements, actual_label) FailedMatchData.new("#{actual.label} contains exactly #{expected.label}", [ LabeledValue.new("Not #{expected_elements.inspect}", "expected"), diff --git a/src/spectator/matchers/matcher.cr b/src/spectator/matchers/matcher.cr index 0d4e1dd..9120909 100644 --- a/src/spectator/matchers/matcher.cr +++ b/src/spectator/matchers/matcher.cr @@ -1,6 +1,4 @@ -require "../test_value" -require "./failed_match_data" -require "./successful_match_data" +require "./match_data" module Spectator::Matchers # Common base class for all expectation conditions. @@ -16,54 +14,8 @@ module Spectator::Matchers # The phrasing should be such that it reads "it ___." abstract def description : String - # Message displayed when the matcher isn't satisifed. - # This is only called when `#matches?` returns false. - private abstract def failure_message(actual) : String + abstract def match(actual) : MatchData - # Message displayed when the matcher isn't satisifed and is negated. - # This is only called when `#does_not_match?` returns false. - # - # A default implementation of this method is provided, - # which causes compilation to fail. - # If the matcher supports negation, it must override this method. - private def failure_message_when_negated(actual) : String - {% raise "Negation with #{@type.name} is not supported." %} - end - - # Checks whether the matcher is satisifed. - private abstract def match?(actual) : Bool - - # If the expectation is negated, then this method is called instead of `#match?`. - # The default implementation of this method is to invert the result of `#match?`. - # If the matcher requires custom handling of negated matches, - # then this method should be overriden. - # Remember to override `#failure_message_when_negated` as well. - private def does_not_match?(actual) : Bool - !matches?(actual) - end - - private def values(actual) : Array(LabeledValue) - [LabeledValue.new(actual.value.inspect, "actual")] - end - - private def negated_values(actual) : Array(LabeledValue) - values - end - - def match(actual) - if match?(actual) - SuccessfulMatchData.new - else - FailedMatchData.new(failure_message(actual), values(actual)) - end - end - - def negated_match(actual) - if does_not_match?(actual) - SuccessfulMatchData.new - else - FailedMatchData.new(failure_message_when_negated(actual), negated_values(actual)) - end - end + abstract def negated_match(actual) : MatchData end end diff --git a/src/spectator/matchers/standard_matcher.cr b/src/spectator/matchers/standard_matcher.cr new file mode 100644 index 0000000..491e21d --- /dev/null +++ b/src/spectator/matchers/standard_matcher.cr @@ -0,0 +1,59 @@ +require "../test_value" +require "./failed_match_data" +require "./matcher" +require "./successful_match_data" + +module Spectator::Matchers + # Provides common methods for matchers. + abstract struct StandardMatcher < Matcher + # Message displayed when the matcher isn't satisifed. + # This is only called when `#matches?` returns false. + private abstract def failure_message(actual) : String + + # Message displayed when the matcher isn't satisifed and is negated. + # This is only called when `#does_not_match?` returns false. + # + # A default implementation of this method is provided, + # which causes compilation to fail. + # If the matcher supports negation, it must override this method. + private def failure_message_when_negated(actual) : String + {% raise "Negation with #{@type.name} is not supported." %} + end + + # Checks whether the matcher is satisifed. + private abstract def match?(actual) : Bool + + # If the expectation is negated, then this method is called instead of `#match?`. + # The default implementation of this method is to invert the result of `#match?`. + # If the matcher requires custom handling of negated matches, + # then this method should be overriden. + # Remember to override `#failure_message_when_negated` as well. + private def does_not_match?(actual) : Bool + !matches?(actual) + end + + private def values(actual) : Array(LabeledValue) + [LabeledValue.new(actual.value.inspect, "actual")] + end + + private def negated_values(actual) : Array(LabeledValue) + values + end + + def match(actual) + if match?(actual) + SuccessfulMatchData.new + else + FailedMatchData.new(failure_message(actual), values(actual)) + end + end + + def negated_match(actual) + if does_not_match?(actual) + SuccessfulMatchData.new + else + FailedMatchData.new(failure_message_when_negated(actual), negated_values(actual)) + end + end + end +end diff --git a/src/spectator/matchers/value_matcher.cr b/src/spectator/matchers/value_matcher.cr index 7e09a08..7c5e0ca 100644 --- a/src/spectator/matchers/value_matcher.cr +++ b/src/spectator/matchers/value_matcher.cr @@ -1,9 +1,9 @@ -require "./matcher" +require "./standard_matcher" module Spectator::Matchers # Category of matcher that uses a value. # Matchers of this type expect that a SUT applies to the value in some way. - abstract struct ValueMatcher(ExpectedType) < Matcher + abstract struct ValueMatcher(ExpectedType) < StandardMatcher # Expected value. # Sub-types may use this value to test the expectation and generate message strings. private getter expected : TestValue(ExpectedType)