From d5fd21702a6890c7824e0234680a753cdc76ddec Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Sat, 10 Aug 2019 10:50:48 -0600 Subject: [PATCH] Add matcher docs --- src/spectator/matchers/array_matcher.cr | 20 ++++++++++++++++ src/spectator/matchers/attributes_matcher.cr | 10 ++++++++ src/spectator/matchers/case_matcher.cr | 17 ++++++++++++++ src/spectator/matchers/collection_matcher.cr | 17 ++++++++++++++ src/spectator/matchers/contain_matcher.cr | 21 ++++++++++++++++- src/spectator/matchers/empty_matcher.cr | 17 ++++++++++++++ src/spectator/matchers/end_with_matcher.cr | 16 +++++++++++++ src/spectator/matchers/equality_matcher.cr | 17 ++++++++++++++ src/spectator/matchers/exception_matcher.cr | 9 ++++++++ .../matchers/greater_than_equal_matcher.cr | 21 +++++++++++++++++ .../matchers/greater_than_matcher.cr | 23 ++++++++++++++++++- src/spectator/matchers/have_key_matcher.cr | 19 +++++++++++++++ src/spectator/matchers/have_matcher.cr | 19 +++++++++++++++ .../matchers/have_predicate_matcher.cr | 23 +++++++++++++++++++ src/spectator/matchers/have_value_matcher.cr | 19 +++++++++++++++ src/spectator/matchers/inequality_matcher.cr | 21 +++++++++++++++++ .../matchers/less_than_equal_matcher.cr | 21 +++++++++++++++++ src/spectator/matchers/less_than_matcher.cr | 21 +++++++++++++++++ src/spectator/matchers/nil_matcher.cr | 17 ++++++++++++++ src/spectator/matchers/predicate_matcher.cr | 23 +++++++++++++++++++ src/spectator/matchers/range_matcher.cr | 21 +++++++++++++++++ src/spectator/matchers/reference_matcher.cr | 17 ++++++++++++++ src/spectator/matchers/respond_matcher.cr | 9 ++++++++ src/spectator/matchers/size_matcher.cr | 21 +++++++++++++++++ src/spectator/matchers/size_of_matcher.cr | 21 +++++++++++++++++ src/spectator/matchers/standard_matcher.cr | 3 ++- src/spectator/matchers/start_with_matcher.cr | 20 +++++++++++++++- src/spectator/matchers/truthy_matcher.cr | 23 +++++++++++++++++++ src/spectator/matchers/type_matcher.cr | 21 +++++++++++++++++ .../matchers/unordered_array_matcher.cr | 8 +++++++ 30 files changed, 531 insertions(+), 4 deletions(-) diff --git a/src/spectator/matchers/array_matcher.cr b/src/spectator/matchers/array_matcher.cr index e03f584..f3c3940 100644 --- a/src/spectator/matchers/array_matcher.cr +++ b/src/spectator/matchers/array_matcher.cr @@ -7,15 +7,21 @@ 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) < Matcher + # Expected value and label. private getter expected + # Creates the matcher with an expected value. def initialize(@expected : TestValue(ExpectedType)) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "contains exactly #{expected.label}" end + # Actually performs the test against the expression. def match(actual : TestExpression(T)) : MatchData forall T actual_elements = actual.value.to_a expected_elements = expected.value.to_a @@ -31,6 +37,8 @@ module Spectator::Matchers end end + # Performs the test against the expression, but inverted. + # A successful match with `#match` should normally fail for this method, and vice-versa. def negated_match(actual : TestExpression(T)) : MatchData forall T actual_elements = actual.value.to_a expected_elements = expected.value.to_a @@ -45,14 +53,23 @@ module Spectator::Matchers end end + # Ensures the arrays elements are compared in order. + # This is the default behavior for the matcher. def in_order self end + # Specifies that the arrays elements can be compared in any order. + # The elements can be in a different order, but both arrays must have the same elements. def in_any_order UnorderedArrayMatcher.new(expected) end + # Compares two arrays to determine whether they contain the same elements, and in the same order. + # If the arrays are the same, then `true` is returned. + # If they are different, `false` or an integer is returned. + # `false` is returned when the sizes of the arrays don't match. + # An integer is returned, that is the index of the mismatched elements in the arrays. private def compare_arrays(expected_elements, actual_elements) if expected_elements.size == actual_elements.size index = 0 @@ -66,6 +83,7 @@ module Spectator::Matchers end end + # Produces match data for a failure when the array sizes differ. private def failed_size_mismatch(expected_elements, actual_elements, actual_label) FailedMatchData.new("#{actual_label} does not contain exactly #{expected.label} (size mismatch)", expected: expected_elements.inspect, @@ -75,6 +93,7 @@ module Spectator::Matchers ) end + # Produces match data for a failure when the array content is mismatched. private def failed_content_mismatch(expected_elements, actual_elements, index, actual_label) FailedMatchData.new("#{actual_label} does not contain exactly #{expected.label} (element mismatch)", expected: expected_elements[index].inspect, @@ -83,6 +102,7 @@ module Spectator::Matchers ) end + # Produces match data for a failure when the arrays are identical, but they shouldn't be (negation). private def failed_content_identical(expected_elements, actual_elements, actual_label) FailedMatchData.new("#{actual_label} contains exactly #{expected.label}", expected: "Not #{expected_elements.inspect}", diff --git a/src/spectator/matchers/attributes_matcher.cr b/src/spectator/matchers/attributes_matcher.cr index 437b80f..a447d4a 100644 --- a/src/spectator/matchers/attributes_matcher.cr +++ b/src/spectator/matchers/attributes_matcher.cr @@ -10,15 +10,21 @@ module Spectator::Matchers # Each key in the tuple is the attribute/method name, # and the corresponding value is the expected value to match against. struct AttributesMatcher(ExpectedType) < Matcher + # Expected value and label. private getter expected + # Creates the matcher with an expected value. def initialize(@expected : TestValue(ExpectedType)) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "has attributes #{expected.label}" end + # Actually performs the test against the expression. def match(actual : TestExpression(T)) : MatchData forall T snapshot = snapshot_values(actual.value) if match?(snapshot) @@ -28,6 +34,8 @@ module Spectator::Matchers end end + # Performs the test against the expression, but inverted. + # A successful match with `#match` should normally fail for this method, and vice-versa. def negated_match(actual : TestExpression(T)) : MatchData forall T snapshot = snapshot_values(actual.value) if match?(snapshot) @@ -49,6 +57,7 @@ module Spectator::Matchers {% end %} end + # Checks if all attributes from the snapshot of them are satisified. private def match?(snapshot) # Test that every attribute has the expected value. {% for attribute in ExpectedType.keys %} @@ -59,6 +68,7 @@ module Spectator::Matchers true end + # Produces the tuple for the failed match data from a snapshot of the attributes. private def values(snapshot) {% begin %} { diff --git a/src/spectator/matchers/case_matcher.cr b/src/spectator/matchers/case_matcher.cr index c4283f6..332212d 100644 --- a/src/spectator/matchers/case_matcher.cr +++ b/src/spectator/matchers/case_matcher.cr @@ -4,18 +4,35 @@ module Spectator::Matchers # Common matcher that tests whether two values semantically equal each other. # The values are compared with the === operator. struct CaseMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) expected.value === actual.value end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "matches #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} does not match #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} matched #{expected.label}" end diff --git a/src/spectator/matchers/collection_matcher.cr b/src/spectator/matchers/collection_matcher.cr index 2a16822..ba533da 100644 --- a/src/spectator/matchers/collection_matcher.cr +++ b/src/spectator/matchers/collection_matcher.cr @@ -5,18 +5,35 @@ require "./value_matcher" module Spectator::Matchers # Matcher for checking that a value is in a collection of other values. struct CollectionMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) expected.value.includes?(actual.value) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "is in #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is not in #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is in #{expected.label}" end diff --git a/src/spectator/matchers/contain_matcher.cr b/src/spectator/matchers/contain_matcher.cr index fa6d65a..4b31fc6 100644 --- a/src/spectator/matchers/contain_matcher.cr +++ b/src/spectator/matchers/contain_matcher.cr @@ -4,24 +4,43 @@ module Spectator::Matchers # Matcher that tests whether a value, such as a `String` or `Array`, contains one or more values. # The values are checked with the `includes?` method. struct ContainMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) expected.value.all? do |item| actual.value.includes?(item) end end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "contains #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} does not match #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} contains #{expected.label}" end - + + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { subset: expected.value.inspect, diff --git a/src/spectator/matchers/empty_matcher.cr b/src/spectator/matchers/empty_matcher.cr index 4db31f9..16171b0 100644 --- a/src/spectator/matchers/empty_matcher.cr +++ b/src/spectator/matchers/empty_matcher.cr @@ -4,18 +4,35 @@ module Spectator::Matchers # Matcher that tests whether a collection is empty. # The values are checked with the `empty?` method. struct EmptyMatcher < StandardMatcher + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) actual.value.empty? end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "is empty" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is not empty" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is empty" end diff --git a/src/spectator/matchers/end_with_matcher.cr b/src/spectator/matchers/end_with_matcher.cr index 3dbd9e6..448d1e6 100644 --- a/src/spectator/matchers/end_with_matcher.cr +++ b/src/spectator/matchers/end_with_matcher.cr @@ -7,15 +7,21 @@ module Spectator::Matchers # The `ends_with?` method is used if it's defined on the actual type. # Otherwise, it is treated as an `Indexable` and the `last` value is compared against. struct EndWithMatcher(ExpectedType) < Matcher + # Expected value and label. private getter expected + # Creates the matcher with an expected value. def initialize(@expected : TestValue(ExpectedType)) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "ends with #{expected.label}" end + # Actually performs the test against the expression. def match(actual : TestExpression(T)) : MatchData forall T if (value = actual.value).responds_to?(:ends_with?) match_ends_with(value, actual.label) @@ -24,6 +30,8 @@ module Spectator::Matchers end end + # Checks whether the actual value ends with the expected value. + # This method expects (and uses) the `#ends_with?` method on the value. private def match_ends_with(actual_value, actual_label) if actual_value.ends_with?(expected.value) SuccessfulMatchData.new @@ -35,6 +43,8 @@ module Spectator::Matchers end end + # Checks whether the last element of the value is the expected value. + # This method expects that the actual value is a set (enumerable). private def match_last(actual_value, actual_label) list = actual_value.to_a last = list.last @@ -50,6 +60,8 @@ module Spectator::Matchers end end + # Performs the test against the expression, but inverted. + # A successful match with `#match` should normally fail for this method, and vice-versa. def negated_match(actual : TestExpression(T)) : MatchData forall T if actual.value.responds_to?(:ends_with?) negated_match_ends_with(actual) @@ -58,6 +70,8 @@ module Spectator::Matchers end end + # Checks whether the actual value does not end with the expected value. + # This method expects (and uses) the `#ends_with?` method on the value. private def negated_match_ends_with(actual) if actual.value.ends_with?(expected.value) FailedMatchData.new("#{actual.label} ends with #{expected.label} (using #ends_with?)", @@ -69,6 +83,8 @@ module Spectator::Matchers end end + # Checks whether the last element of the value is not the expected value. + # This method expects that the actual value is a set (enumerable). private def negated_match_last(actual) list = actual.value.to_a last = list.last diff --git a/src/spectator/matchers/equality_matcher.cr b/src/spectator/matchers/equality_matcher.cr index ce95be1..81f0cbb 100644 --- a/src/spectator/matchers/equality_matcher.cr +++ b/src/spectator/matchers/equality_matcher.cr @@ -4,18 +4,35 @@ module Spectator::Matchers # Common matcher that tests whether two values equal each other. # The values are compared with the == operator. struct EqualityMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) expected.value == actual.value end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "equals #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} does not equal #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} equals #{expected.label}" end diff --git a/src/spectator/matchers/exception_matcher.cr b/src/spectator/matchers/exception_matcher.cr index 3641c15..981845c 100644 --- a/src/spectator/matchers/exception_matcher.cr +++ b/src/spectator/matchers/exception_matcher.cr @@ -6,15 +6,21 @@ require "./successful_match_data" module Spectator::Matchers # Matcher that tests whether an exception is raised. struct ExceptionMatcher(ExceptionType, ExpectedType) < Matcher + # Expected value and label. private getter expected + # Creates the matcher with no expectation of the message. def initialize @expected = TestValue.new(nil, ExceptionType.to_s) end + # Creates the matcher with an expected message. def initialize(@expected : TestValue(ExpectedType)) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description if (message = @expected) "raises #{ExceptionType} with message #{message}" @@ -23,6 +29,7 @@ module Spectator::Matchers end end + # Actually performs the test against the expression. def match(actual : TestExpression(T)) : MatchData forall T exception = capture_exception { actual.value } if exception.nil? @@ -52,6 +59,8 @@ module Spectator::Matchers end end + # Performs the test against the expression, but inverted. + # A successful match with `#match` should normally fail for this method, and vice-versa. def negated_match(actual : TestExpression(T)) : MatchData forall T exception = capture_exception { actual.value } if exception.nil? diff --git a/src/spectator/matchers/greater_than_equal_matcher.cr b/src/spectator/matchers/greater_than_equal_matcher.cr index 1a2ef80..7d6d7f8 100644 --- a/src/spectator/matchers/greater_than_equal_matcher.cr +++ b/src/spectator/matchers/greater_than_equal_matcher.cr @@ -4,22 +4,41 @@ module Spectator::Matchers # Matcher that tests whether one value is greater than or equal to another. # The values are compared with the >= operator. struct GreaterThanEqualMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) actual.value >= expected.value end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "greater than or equal to #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is less than #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is greater than or equal to #{expected.label}" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { expected: ">= #{expected.value.inspect}", @@ -27,6 +46,8 @@ module Spectator::Matchers } end + # Additional information about the match failure when negated. + # The return value is a NamedTuple with Strings for each value. private def negated_values(actual) { expected: "< #{expected.value.inspect}", diff --git a/src/spectator/matchers/greater_than_matcher.cr b/src/spectator/matchers/greater_than_matcher.cr index 12ec50f..81d988d 100644 --- a/src/spectator/matchers/greater_than_matcher.cr +++ b/src/spectator/matchers/greater_than_matcher.cr @@ -4,29 +4,50 @@ module Spectator::Matchers # Matcher that tests whether one value is greater than another. # The values are compared with the > operator. struct GreaterThanMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) actual.value > expected.value end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "greater than #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is less than or equal to #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is greater than #{expected.label}" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { expected: "> #{expected.value.inspect}", actual: actual.value.inspect, } end - + + # Additional information about the match failure when negated. + # The return value is a NamedTuple with Strings for each value. private def negated_values(actual) { expected: "<= #{expected.value.inspect}", diff --git a/src/spectator/matchers/have_key_matcher.cr b/src/spectator/matchers/have_key_matcher.cr index 771f5ab..1021827 100644 --- a/src/spectator/matchers/have_key_matcher.cr +++ b/src/spectator/matchers/have_key_matcher.cr @@ -4,22 +4,41 @@ module Spectator::Matchers # Matcher that tests whether a `Hash` (or similar type) has a given key. # The set is checked with the `has_key?` method. struct HaveKeyMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) actual.value.has_key?(expected.value) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "has key #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} does not have key #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} has key #{expected.label}" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) actual_value = actual.value set = actual_value.responds_to?(:keys) ? actual_value.keys : actual_value diff --git a/src/spectator/matchers/have_matcher.cr b/src/spectator/matchers/have_matcher.cr index 3886e51..c2d6af9 100644 --- a/src/spectator/matchers/have_matcher.cr +++ b/src/spectator/matchers/have_matcher.cr @@ -5,6 +5,7 @@ module Spectator::Matchers # For a `String`, the `includes?` method is used. # Otherwise, it expects an `Enumerable` and iterates over each item until === is true. struct HaveMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) if (value = actual.value).is_a?(String) match_string?(value) @@ -32,18 +33,36 @@ module Spectator::Matchers end end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "includes #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} does not include #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} includes #{expected.label}" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { subset: expected.value.inspect, diff --git a/src/spectator/matchers/have_predicate_matcher.cr b/src/spectator/matchers/have_predicate_matcher.cr index 525f157..8b9ff1f 100644 --- a/src/spectator/matchers/have_predicate_matcher.cr +++ b/src/spectator/matchers/have_predicate_matcher.cr @@ -7,15 +7,21 @@ module Spectator::Matchers # 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) + # Expected value and label. private getter expected + # Creates the matcher with a expected values. def initialize(@expected : TestValue(ExpectedType)) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "has #{expected.label}" end + # Actually performs the test against the expression. def match(actual : TestExpression(T)) : MatchData forall T snapshot = snapshot_values(actual.value) if match?(snapshot) @@ -25,6 +31,8 @@ module Spectator::Matchers end end + # Performs the test against the expression, but inverted. + # A successful match with `#match` should normally fail for this method, and vice-versa. def negated_match(actual : TestExpression(T)) : MatchData forall T snapshot = snapshot_values(actual.value) if match?(snapshot) @@ -34,10 +42,23 @@ module Spectator::Matchers end end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} does not have #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} has #{expected.label}" end @@ -54,6 +75,7 @@ module Spectator::Matchers {% end %} end + # Produces the tuple for the failed match data from a snapshot of the predicate methods. private def values(snapshot) {% begin %} { @@ -64,6 +86,7 @@ module Spectator::Matchers {% end %} end + # Checks if all predicate methods from the snapshot of them are satisified. private def match?(snapshot) # Test each predicate and immediately return false if one is false. {% for attribute in ExpectedType.keys %} diff --git a/src/spectator/matchers/have_value_matcher.cr b/src/spectator/matchers/have_value_matcher.cr index 7a44f14..429e481 100644 --- a/src/spectator/matchers/have_value_matcher.cr +++ b/src/spectator/matchers/have_value_matcher.cr @@ -4,22 +4,41 @@ module Spectator::Matchers # Matcher that tests whether a `Hash` (or similar type) has a given value. # The set is checked with the `has_value?` method. struct HaveValueMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) actual.value.has_value?(expected.value) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "has value #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} does not have value #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} has value #{expected.label}" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) actual_value = actual.value set = actual_value.responds_to?(:values) ? actual_value.values : actual_value diff --git a/src/spectator/matchers/inequality_matcher.cr b/src/spectator/matchers/inequality_matcher.cr index c1bd6cd..caa4d90 100644 --- a/src/spectator/matchers/inequality_matcher.cr +++ b/src/spectator/matchers/inequality_matcher.cr @@ -4,22 +4,41 @@ module Spectator::Matchers # Matcher that tests whether two values do not equal each other. # The values are compared with the != operator. struct InequalityMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) expected.value != actual.value end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "is not equal to #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is equal to #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is not equal to #{expected.label}" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { expected: "Not #{expected.value.inspect}", @@ -27,6 +46,8 @@ module Spectator::Matchers } end + # Additional information about the match failure when negated. + # The return value is a NamedTuple with Strings for each value. private def negated_values(actual) { expected: expected.value.inspect, diff --git a/src/spectator/matchers/less_than_equal_matcher.cr b/src/spectator/matchers/less_than_equal_matcher.cr index da43df1..c3c413a 100644 --- a/src/spectator/matchers/less_than_equal_matcher.cr +++ b/src/spectator/matchers/less_than_equal_matcher.cr @@ -4,22 +4,41 @@ module Spectator::Matchers # Matcher that tests whether one value is less than or equal to another. # The values are compared with the <= operator. struct LessThanEqualMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) actual.value <= expected.value end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "less than or equal to #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is greater than #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is less than or equal to #{expected.label}" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { expected: "<= #{expected.value.inspect}", @@ -27,6 +46,8 @@ module Spectator::Matchers } end + # Additional information about the match failure when negated. + # The return value is a NamedTuple with Strings for each value. private def negated_values(actual) { expected: "> #{expected.value.inspect}", diff --git a/src/spectator/matchers/less_than_matcher.cr b/src/spectator/matchers/less_than_matcher.cr index 0c37302..185532d 100644 --- a/src/spectator/matchers/less_than_matcher.cr +++ b/src/spectator/matchers/less_than_matcher.cr @@ -4,22 +4,41 @@ module Spectator::Matchers # Matcher that tests whether one value is less than another. # The values are compared with the < operator. struct LessThanMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) actual.value < expected.value end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "less than #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is greater than or equal to #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is less than #{expected.label}" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { expected: "< #{expected.value.inspect}", @@ -27,6 +46,8 @@ module Spectator::Matchers } end + # Additional information about the match failure when negated. + # The return value is a NamedTuple with Strings for each value. private def negated_values(actual) { expected: ">= #{expected.value.inspect}", diff --git a/src/spectator/matchers/nil_matcher.cr b/src/spectator/matchers/nil_matcher.cr index c97a663..6fc109b 100644 --- a/src/spectator/matchers/nil_matcher.cr +++ b/src/spectator/matchers/nil_matcher.cr @@ -4,18 +4,35 @@ module Spectator::Matchers # Common matcher that tests whether a value is nil. # The `Object#nil?` method is used for this. struct NilMatcher < StandardMatcher + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) actual.value.nil? end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "is nil" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is not nil" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is nil" end diff --git a/src/spectator/matchers/predicate_matcher.cr b/src/spectator/matchers/predicate_matcher.cr index 5ab5582..9d58073 100644 --- a/src/spectator/matchers/predicate_matcher.cr +++ b/src/spectator/matchers/predicate_matcher.cr @@ -6,15 +6,21 @@ module Spectator::Matchers # Each key in the tuple is a predicate (without the '?') to test. # Each value is a a `Tuple` of arguments to pass to the predicate method. struct PredicateMatcher(ExpectedType) < Matcher + # Expected value and label. private getter expected + # Creates the matcher with a expected values. def initialize(@expected : TestValue(ExpectedType)) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "is #{expected.label}" end + # Actually performs the test against the expression. def match(actual : TestExpression(T)) : MatchData forall T snapshot = snapshot_values(actual.value) if match?(snapshot) @@ -24,6 +30,8 @@ module Spectator::Matchers end end + # Performs the test against the expression, but inverted. + # A successful match with `#match` should normally fail for this method, and vice-versa. def negated_match(actual : TestExpression(T)) : MatchData forall T snapshot = snapshot_values(actual.value) if match?(snapshot) @@ -33,10 +41,23 @@ module Spectator::Matchers end end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is not #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is #{expected.label}" end @@ -53,6 +74,7 @@ module Spectator::Matchers {% end %} end + # Produces the tuple for the failed match data from a snapshot of the predicate methods. private def values(snapshot) {% begin %} { @@ -63,6 +85,7 @@ module Spectator::Matchers {% end %} end + # Checks if all predicate methods from the snapshot of them are satisified. private def match?(snapshot) # Test each predicate and immediately return false if one is false. {% for attribute in ExpectedType.keys %} diff --git a/src/spectator/matchers/range_matcher.cr b/src/spectator/matchers/range_matcher.cr index 7ec238e..98a0bee 100644 --- a/src/spectator/matchers/range_matcher.cr +++ b/src/spectator/matchers/range_matcher.cr @@ -4,22 +4,41 @@ module Spectator::Matchers # Matcher that tests whether a value is in a given range. # The `Range#includes?` method is used for this check. struct RangeMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) expected.value.includes?(actual.value) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "is in #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is not in #{expected.label} (#{exclusivity})" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is in #{expected.label} (#{exclusivity})" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { lower: ">= #{range.begin.inspect}", @@ -28,6 +47,8 @@ module Spectator::Matchers } end + # Additional information about the match failure when negated. + # The return value is a NamedTuple with Strings for each value. private def negated_values(actual) { lower: "< #{range.begin.inspect}", diff --git a/src/spectator/matchers/reference_matcher.cr b/src/spectator/matchers/reference_matcher.cr index 42d84d3..bc9d709 100644 --- a/src/spectator/matchers/reference_matcher.cr +++ b/src/spectator/matchers/reference_matcher.cr @@ -4,18 +4,35 @@ module Spectator::Matchers # Matcher that tests whether two references are the same. # The values are compared with the `Reference#same?` method. struct ReferenceMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) expected.value.same?(actual.value) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "is #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is not #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is #{expected.label}" end diff --git a/src/spectator/matchers/respond_matcher.cr b/src/spectator/matchers/respond_matcher.cr index 00d63bb..a3869b4 100644 --- a/src/spectator/matchers/respond_matcher.cr +++ b/src/spectator/matchers/respond_matcher.cr @@ -6,15 +6,20 @@ module Spectator::Matchers # 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 + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "responds to #{label}" end + # Generated, user-friendly, string for the expected value. private def label # Prefix every method name with # and join them with commas. {{ExpectedType.keys.map { |e| "##{e}".id }.splat.stringify}} end + # Actually performs the test against the expression. def match(actual : TestExpression(T)) : MatchData forall T snapshot = snapshot_values(actual.value) if match?(snapshot) @@ -24,6 +29,8 @@ module Spectator::Matchers end end + # Performs the test against the expression, but inverted. + # A successful match with `#match` should normally fail for this method, and vice-versa. def negated_match(actual : TestExpression(T)) : MatchData forall T snapshot = snapshot_values(actual.value) if match?(snapshot) @@ -45,6 +52,7 @@ module Spectator::Matchers {% end %} end + # Produces the tuple for the failed match data from a snapshot of the results. private def values(snapshot) {% begin %} { @@ -55,6 +63,7 @@ module Spectator::Matchers {% end %} end + # Checks if all results from the snapshot are satisified. private def match?(snapshot) # The snapshot did the hard work. # Here just check if all values are true. diff --git a/src/spectator/matchers/size_matcher.cr b/src/spectator/matchers/size_matcher.cr index f4d37c9..f72b3e2 100644 --- a/src/spectator/matchers/size_matcher.cr +++ b/src/spectator/matchers/size_matcher.cr @@ -4,22 +4,41 @@ module Spectator::Matchers # Matcher that tests whether a set has a specified number of elements. # The set's `#size` method is used for this check. struct SizeMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) expected.value == actual.value.size end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "has size #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} does not have #{expected.label} elements" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} has #{expected.label} elements" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { expected: expected.value.inspect, @@ -27,6 +46,8 @@ module Spectator::Matchers } end + # Additional information about the match failure when negated. + # The return value is a NamedTuple with Strings for each value. private def negated_values(actual) { expected: "Not #{expected.value.inspect}", diff --git a/src/spectator/matchers/size_of_matcher.cr b/src/spectator/matchers/size_of_matcher.cr index fefd10a..4339791 100644 --- a/src/spectator/matchers/size_of_matcher.cr +++ b/src/spectator/matchers/size_of_matcher.cr @@ -4,22 +4,41 @@ module Spectator::Matchers # Matcher that tests whether a set has the same number of elements as another set. # The set's `#size` method is used for this check. struct SizeOfMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) expected.value.size == actual.value.size end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "is the same size as #{expected.label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is not the same size as #{expected.label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is the same size as #{expected.label}" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { expected: expected.value.size.inspect, @@ -27,6 +46,8 @@ module Spectator::Matchers } end + # Additional information about the match failure when negated. + # The return value is a NamedTuple with Strings for each value. private def negated_values(actual) { expected: "Not #{expected.value.size.inspect}", diff --git a/src/spectator/matchers/standard_matcher.cr b/src/spectator/matchers/standard_matcher.cr index afb688b..18753bb 100644 --- a/src/spectator/matchers/standard_matcher.cr +++ b/src/spectator/matchers/standard_matcher.cr @@ -26,6 +26,7 @@ module Spectator::Matchers private abstract def failure_message(actual : TestExpression(T)) : String forall T # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. # # This is only called when `#does_not_match?` returns false. # @@ -39,7 +40,7 @@ module Spectator::Matchers {% raise "Negation with #{@type.name} is not supported." %} end - # Checks whether the matcher is satisifed. + # Checks whether the matcher is satisifed with the expression given to it. private abstract def match?(actual : TestExpression(T)) : Bool forall T # If the expectation is negated, then this method is called instead of `#match?`. diff --git a/src/spectator/matchers/start_with_matcher.cr b/src/spectator/matchers/start_with_matcher.cr index 2e7961f..15219c7 100644 --- a/src/spectator/matchers/start_with_matcher.cr +++ b/src/spectator/matchers/start_with_matcher.cr @@ -1,19 +1,27 @@ -require "./value_matcher" + + # Checks whether the last element of the value is the expected value. +# This method expects that the actual value is a set (enumerable).require "./value_matcher" module Spectator::Matchers # Matcher that tests whether a value, such as a `String` or `Array`, starts with a value. # The `starts_with?` method is used if it's defined on the actual type. # Otherwise, it is treated as an `Enumerable` and the `first` value is compared against. struct StartWithMatcher(ExpectedType) < Matcher + # Expected value and label. private getter expected + # Creates the matcher with an expected value. def initialize(@expected : TestValue(ExpectedType)) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "starts with #{expected.label}" end + # Actually performs the test against the expression. def match(actual : TestExpression(T)) : MatchData forall T if (value = actual.value).responds_to?(:starts_with?) match_starts_with(value, actual.label) @@ -22,6 +30,8 @@ module Spectator::Matchers end end + # Checks whether the actual value starts with the expected value. + # This method expects (and uses) the `#starts_with?` method on the value. private def match_starts_with(actual_value, actual_label) if actual_value.starts_with?(expected.value) SuccessfulMatchData.new @@ -33,6 +43,8 @@ module Spectator::Matchers end end + # Checks whether the first element of the value is the expected value. + # This method expects that the actual value is a set (enumerable). private def match_first(actual_value, actual_label) list = actual_value.to_a first = list.first @@ -48,6 +60,8 @@ module Spectator::Matchers end end + # Performs the test against the expression, but inverted. + # A successful match with `#match` should normally fail for this method, and vice-versa. def negated_match(actual : TestExpression(T)) : MatchData forall T if (value = actual.value).responds_to?(:starts_with?) negated_match_starts_with(value, actual.label) @@ -56,6 +70,8 @@ module Spectator::Matchers end end + # Checks whether the actual value does not start with the expected value. + # This method expects (and uses) the `#starts_with?` method on the value. private def negated_match_starts_with(actual_value, actual_label) if actual_value.starts_with?(expected.value) FailedMatchData.new("#{actual_label} starts with #{expected.label} (using #starts_with?)", @@ -67,6 +83,8 @@ module Spectator::Matchers end end + # Checks whether the first element of the value is not the expected value. + # This method expects that the actual value is a set (enumerable). private def negated_match_first(actual_value, actual_label) list = actual_value.to_a first = list.first diff --git a/src/spectator/matchers/truthy_matcher.cr b/src/spectator/matchers/truthy_matcher.cr index 3629a31..46387c5 100644 --- a/src/spectator/matchers/truthy_matcher.cr +++ b/src/spectator/matchers/truthy_matcher.cr @@ -15,30 +15,51 @@ module Spectator::Matchers def initialize(@truthy : Bool = true) end + # Generated, user-friendly, string for the expected value. private def label @truthy ? "truthy" : "falsey" end + # Generated, user-friendly, string for the unexpected value. private def negated_label @truthy ? "falsey" : "truthy" end + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) @truthy == !!actual.value end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "is #{label}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is #{negated_label}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is #{label}" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { expected: @truthy ? "Not false or nil" : "false or nil", @@ -47,6 +68,8 @@ module Spectator::Matchers } end + # Additional information about the match failure when negated. + # The return value is a NamedTuple with Strings for each value. private def negated_values(actual) { expected: @truthy ? "false or nil" : "Not false or nil", diff --git a/src/spectator/matchers/type_matcher.cr b/src/spectator/matchers/type_matcher.cr index 16ee14c..928e07b 100644 --- a/src/spectator/matchers/type_matcher.cr +++ b/src/spectator/matchers/type_matcher.cr @@ -4,22 +4,41 @@ module Spectator::Matchers # Matcher that tests a value is of a specified type. # The values are compared with the `Object#is_a?` method. struct TypeMatcher(Expected) < StandardMatcher + # Checks whether the matcher is satisifed with the expression given to it. private def match?(actual) actual.value.is_a?(Expected) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "is as #{Expected}" end + # Message displayed when the matcher isn't satisifed. + # + # This is only called when `#match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message(actual) "#{actual.label} is not a #{Expected}" end + # Message displayed when the matcher isn't satisifed and is negated. + # This is essentially what would satisfy the matcher if it wasn't negated. + # + # This is only called when `#does_not_match?` returns false. + # + # The message should typically only contain the test expression labels. + # Actual values should be returned by `#values`. private def failure_message_when_negated(actual) "#{actual.label} is a #{Expected}" end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. private def values(actual) { expected: Expected.to_s, @@ -27,6 +46,8 @@ module Spectator::Matchers } end + # Additional information about the match failure when negated. + # The return value is a NamedTuple with Strings for each value. private def negated_values(actual) { expected: "Not #{Expected}", diff --git a/src/spectator/matchers/unordered_array_matcher.cr b/src/spectator/matchers/unordered_array_matcher.cr index acef92f..823ee16 100644 --- a/src/spectator/matchers/unordered_array_matcher.cr +++ b/src/spectator/matchers/unordered_array_matcher.cr @@ -4,15 +4,21 @@ module Spectator::Matchers # Matcher for checking that the contents of one array (or similar type) # has the exact same contents as another, but in any order. struct UnorderedArrayMatcher(ExpectedType) < Matcher + # Expected value and label. private getter expected + # Creates the matcher with an expected value. def initialize(@expected : TestValue(ExpectedType)) end + # Short text about the matcher's purpose. + # This explains what condition satisfies the matcher. + # The description is used when the one-liner syntax is used. def description "contains #{expected.label} in any order" end + # Actually performs the test against the expression. def match(actual : TestExpression(T)) : MatchData forall T actual_elements = actual.value.to_a expected_elements = expected.value.to_a @@ -30,6 +36,8 @@ module Spectator::Matchers end end + # Performs the test against the expression, but inverted. + # A successful match with `#match` should normally fail for this method, and vice-versa. def negated_match(actual : TestExpression(T)) : MatchData forall T actual_elements = actual.value.to_a expected_elements = expected.value.to_a