diff --git a/src/spectator/dsl/matcher_dsl.cr b/src/spectator/dsl/matcher_dsl.cr index e1fa206..06ec640 100644 --- a/src/spectator/dsl/matcher_dsl.cr +++ b/src/spectator/dsl/matcher_dsl.cr @@ -14,5 +14,23 @@ module Spectator::DSL macro eq(expected) ::Spectator::Matchers::EqualityMatcher.new({{expected.stringify}}, {{expected}}) end + + # Indicates that some value should semantically equal another. + # The `===` operator is used for this check. + # This has identical behavior as a `when` condition in a `case` block. + # + # Examples: + # ``` + # expect(1 + 2).to be(3) + # expect(5).to be(Int32) # Using `#be_a` instead is recommened here. + # expect(5).to be(.odd?) # Using `#be_odd` instead is recommended here. + # expect(tuple).to be({1, 2}) + # ``` + # + # See https://crystal-lang.org/reference/syntax_and_semantics/case.html + # for more examples of what could be used here. + macro be(expected) + ::Spectator::Matchers::CaseMatcher.new({{expected.stringify}}, {{expected}}) + end end end diff --git a/src/spectator/matchers/case_matcher.cr b/src/spectator/matchers/case_matcher.cr new file mode 100644 index 0000000..fdcde58 --- /dev/null +++ b/src/spectator/matchers/case_matcher.cr @@ -0,0 +1,25 @@ +require "./value_matcher" + +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) + # 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 : Expectations::ValueExpectationPartial(ActualType)) : Bool forall ActualType + partial.actual === expected + end + + # Describes the condition that satisfies the matcher. + # This is informational and displayed to the end-user. + def message(partial : Expectations::ValueExpectationPartial(ActualType)) : String forall ActualType + "Expected #{partial.label} to semantically equal #{label} (using ===)" + end + + # Describes the condition that won't satsify the matcher. + # This is informational and displayed to the end-user. + def negated_message(partial : Expectations::ValueExpectationPartial(ActualType)) : String forall ActualType + "Expected #{partial.label} to not semantically equal #{label} (using ===)" + end + end +end