From 46875d77704e758970094f55286f8ab71e9a1555 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Sat, 19 Jan 2019 14:08:50 -0700 Subject: [PATCH] Implement `be_a` matcher --- src/spectator/dsl/matcher_dsl.cr | 15 +++++++++++++++ src/spectator/matchers/type_matcher.cr | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/spectator/matchers/type_matcher.cr diff --git a/src/spectator/dsl/matcher_dsl.cr b/src/spectator/dsl/matcher_dsl.cr index d5fad69..b4a6e1b 100644 --- a/src/spectator/dsl/matcher_dsl.cr +++ b/src/spectator/dsl/matcher_dsl.cr @@ -28,5 +28,20 @@ module Spectator::DSL macro be(expected) ::Spectator::Matchers::CaseMatcher.new({{expected.stringify}}, {{expected}}) end + + # Indicates that some value should be of a specified type. + # The `#is_a?` method is used for this check. + # A type name or type union should be used for `expected`. + # + # Examples: + # ``` + # expect("foo").to be_a(String) + # + # x = Random.rand(2) == 0 ? "foobar" : 5 + # expect(x).to be_a(Int32 | String) + # ``` + macro be_a(expected) + ::Spectator::Matchers::TypeMatcher({{expected}}).new({{expected.stringify}}, nil) + end end end diff --git a/src/spectator/matchers/type_matcher.cr b/src/spectator/matchers/type_matcher.cr new file mode 100644 index 0000000..2ee10a4 --- /dev/null +++ b/src/spectator/matchers/type_matcher.cr @@ -0,0 +1,25 @@ +require "./value_matcher" + +module Spectator::Matchers + # Matcher that tests a value is of a specified type. + # The values are compared with the `#is_a?` method. + struct TypeMatcher(Expected) < ValueMatcher(Nil) + # 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.is_a?(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 be a #{label}" + 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 be a #{label}" + end + end +end