From 7cca43029eef3efe70b2ce9316796168b35fb7b8 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Thu, 8 Aug 2019 14:20:21 -0600 Subject: [PATCH] Refactor ExceptionMatcher --- src/spectator/matchers/exception_matcher.cr | 158 ++++++++------------ 1 file changed, 65 insertions(+), 93 deletions(-) diff --git a/src/spectator/matchers/exception_matcher.cr b/src/spectator/matchers/exception_matcher.cr index 738281e..c4b9197 100644 --- a/src/spectator/matchers/exception_matcher.cr +++ b/src/spectator/matchers/exception_matcher.cr @@ -1,11 +1,65 @@ -require "./value_matcher" +require "../test_value" +require "./failed_match_data" +require "./matcher" +require "./successful_match_data" module Spectator::Matchers # Matcher that tests whether an exception is raised. - struct ExceptionMatcher(ExceptionType, ExpectedType) < ValueMatcher(ExpectedType) - # Determines whether the matcher is satisfied with the value given to it. - private def match?(exception) - exception.is_a?(ExceptionType) && (expected.nil? || expected === exception.message) + struct ExceptionMatcher(ExceptionType, ExpectedType) < Matcher + private getter expected + + def initialize + @expected = TestValue.new(nil, ExceptionType.to_s) + end + + def initialize(@expected : TestValue(ExpectedType)) + end + + def match(actual) + exception = capture_exception { actual.value } + case exception + when Nil + FailedMatchData.new("#{actual.label} did not raise", expected: ExpectedType.inspect) + when ExceptionType + if expected.value.nil? || expected.value === exception.message + SuccessfulMatchData.new + else + FailedMatchData.new("#{actual.label} raised #{ExpectedType}, but the message is not #{expected.label}", + "expected type": ExceptionType.inspect, + "actual type": exception.class.inspect, + "expected message": expected.value.inspect, + "actual message": exception.message + ) + end + else + FailedMatchData.new("#{actual.label} did not raise #{ExpectedType}", + expected: ExpectedType.inspect, + actual: exception.class.inspect + ) + end + end + + def negated_match(actual) + exception = capture_exception { actual.value } + case exception + when Nil + SuccessfulMatchData.new + when ExceptionType + if expected.value.nil? + FailedMatchData.new("#{actual.label} raised #{ExpectedType}") + elsif expected.value === exception.message + FailedMatchData.new("#{actual.label} raised #{ExpectedType} with message matching #{expected.label}", + "expected type": ExceptionType.inspect, + "actual type": exception.class.inspect, + "expected message": expected.value.inspect, + "actual message": exception.message + ) + else + SuccessfulMatchData.new + end + else + SuccessfulMatchData.new + end end # Runs a block of code and returns the exception it threw. @@ -20,103 +74,21 @@ module Spectator::Matchers exception end - # Determines whether the matcher is satisfied with the partial given to it. - def match(partial, negated = false) - exception = capture_exception { partial.actual } - matched = match?(exception) - if exception.nil? - MatchData.new(ExceptionType, matched, ExpectedActual.new(expected, label, exception, partial.label)) - else - values = ExpectedActual.new(expected, label, exception, partial.label) - if expected.nil? - MatchData.new(ExceptionType, matched, values) - else - MessageMatchData.new(ExceptionType, matched, values) - end - end - end - - # Creates a new exception matcher with no message check. - def initialize - super(nil, ExceptionType.to_s) - end - - # Creates a new exception matcher with a message check. - def initialize(expected : ExpectedType, label : String) - super(expected, label) - end - # Creates a new exception matcher with no message check. def self.create(exception_type : T.class, label : String) forall T ExceptionMatcher(T, Nil).new end # Creates a new exception matcher with a message check. - def self.create(expected, label : String) - ExceptionMatcher(Exception, typeof(expected)).new(expected, label) + def self.create(value, label : String) + expected = TestValue.new(value, label) + ExceptionMatcher(Exception, typeof(value)).new(expected) end # Creates a new exception matcher with a type and message check. - def self.create(exception_type : T.class, expected, label : String) forall T - ExceptionMatcher(T, typeof(expected)).new(expected, label) - end - - # Match data specific to this matcher. - private struct MatchData(ExceptionType, ExpectedType, ActualType) < MatchData - # Creates the match data. - def initialize(t : ExceptionType.class, matched, @values : ExpectedActual(ExpectedType, ActualType)) - super(matched) - end - - # Information about the match. - def named_tuple - { - "expected type": NegatableMatchDataValue.new(ExceptionType), - "actual type": @values.actual.class, - } - end - - # Describes the condition that satisfies the matcher. - # This is informational and displayed to the end-user. - def message - "#{@values.actual_label} raises #{ExceptionType}" - end - - # Describes the condition that won't satsify the matcher. - # This is informational and displayed to the end-user. - def negated_message - "#{@values.actual_label} does not raise #{ExceptionType}" - end - end - - # Match data specific to this matcher with an expected message. - private struct MessageMatchData(ExceptionType, ExpectedType) < ::Spectator::Matchers::MatchData - # Creates the match data. - def initialize(t : ExceptionType.class, matched, @values : ExpectedActual(ExpectedType, Exception)) - super(matched) - end - - # Information about the match. - def named_tuple - { - "expected type": NegatableMatchDataValue.new(ExceptionType), - "actual type": @values.actual.class, - "expected message": NegatableMatchDataValue.new(@values.expected), - "actual message": @values.actual.message, - } - end - - # Describes the condition that satisfies the matcher. - # This is informational and displayed to the end-user. - def message - "#{@values.actual_label} raises #{ExceptionType} with message #{@values.expected_label}" - end - - # Describes the condition that won't satsify the matcher. - # This is informational and displayed to the end-user. - def negated_message - "#{@values.actual_label} does not raise #{ExceptionType} with message #{@values.expected_label}" - end + def self.create(exception_type : T.class, value, label : String) forall T + expected = TestValue.new(value, label) + ExceptionMatcher(T, typeof(value)).new(expected) end end end