diff --git a/src/spectator/expectations/expectation.cr b/src/spectator/expectations/expectation.cr index 1844269..8d33f53 100644 --- a/src/spectator/expectations/expectation.cr +++ b/src/spectator/expectations/expectation.cr @@ -1,11 +1,50 @@ module Spectator::Expectations - abstract class Expectation - getter? negated : Bool + # Min-in for all expectation types. + # Classes that include this must implement + # the `#satisfied?`, `#message`, and `#negated_message` methods. + module Expectation + # Checks whether the expectation is met. + abstract def satisfied? : Bool - private def initialize(@negated) + # Describes the condition that must be met for the expectation to be satisifed. + abstract def message : String + + # Describes the condition under which the expectation won't be satisifed. + abstract def negated_message : String + + # Evaulates the expectation and produces a result. + # The `negated` flag should be set to true to invert the result. + def eval(negated = false) : Result + success = satisfied? ^ negated + Result.new(success, negated, self) end - abstract def eval : Bool - abstract def message : String + # Information regarding the outcome of an expectation. + class Result + # Indicates whether the expectation was satisifed or not. + getter? successful : Bool + + # Creates the result. + # The expectation is stored so that information from it may be lazy-loaded. + protected def initialize(@successful, @negated : Bool, @expectation : Expectation) + end + + # Description of the condition that satisfies, or meets, the expectation. + def satisfy_message + message(@negated) + end + + # Description of what actually happened when the expectation was evaluated. + def result_message + message(@successful) + end + + # Retrieves the message or negated message from an expectation. + # Set `negated` to true to get the negated message, + # or to false to get the regular message. + private def message(negated) + negated ? @expectation.negated_message : @expectation.message + end + end end end diff --git a/src/spectator/expectations/expectation_registry.cr b/src/spectator/expectations/expectation_registry.cr index 464caad..eb1216c 100644 --- a/src/spectator/expectations/expectation_registry.cr +++ b/src/spectator/expectations/expectation_registry.cr @@ -7,7 +7,7 @@ module Spectator::Expectations private def initialize(@raise_on_failure = true) end - def report(expectation : Expectation) : Nil + def report(result : Expectation::Result) : Nil raise NotImplementedError.new("ExpectationRegistry#report") end @@ -15,11 +15,11 @@ module Spectator::Expectations raise NotImplementedError.new("ExpectationRegistry.current") end - def self.start(example : Example) : ExpectationRegistry + def self.start(example : Example) : Nil raise NotImplementedError.new("ExpectationRegistry.start") end - def self.finish : Nil # TODO: Define return type. + def self.finish : ExpectationResults raise NotImplementedError.new("ExpectationRegistry.finish") end end diff --git a/src/spectator/expectations/expectation_results.cr b/src/spectator/expectations/expectation_results.cr new file mode 100644 index 0000000..85e604f --- /dev/null +++ b/src/spectator/expectations/expectation_results.cr @@ -0,0 +1,6 @@ +module Spectator::Expectations + class ExpectationResults + def initialize(@results : Enumerable(ExpectationResult)) + end + end +end diff --git a/src/spectator/expectations/value_expectation.cr b/src/spectator/expectations/value_expectation.cr index 51b779e..7fab54d 100644 --- a/src/spectator/expectations/value_expectation.cr +++ b/src/spectator/expectations/value_expectation.cr @@ -1,19 +1,26 @@ require "./expectation" module Spectator::Expectations - class ValueExpectation(ActualType, ExpectedType) < Expectation - def initialize(negated, - @partial : ValueExpectationPartial(ActualType), - @matcher : ValueMatcher(ExpectedType)) - super(negated) + class ValueExpectation(ActualType, ExpectedType) + include Expectation + + def initialize(@partial : ValueExpectationPartial(ActualType), + @matcher : Matchers::ValueMatcher(ExpectedType)) end - def eval : Bool - @matcher.match?(@partial) ^ negated? + # Checks whether the expectation is met. + def satisfied? : Bool + @matcher.match?(@partial) end + # Describes the condition that must be met for the expectation to be satisifed. def message : String - negated? ? @matcher.negated_message(@partial) : @matcher.message(@partial) + @matcher.message(@partial) + end + + # Describes the condition under which the expectation won't be satisifed. + def negated_message : String + @matcher.negated_message(@partial) end end end diff --git a/src/spectator/expectations/value_expectation_partial.cr b/src/spectator/expectations/value_expectation_partial.cr index e488669..c264d56 100644 --- a/src/spectator/expectations/value_expectation_partial.cr +++ b/src/spectator/expectations/value_expectation_partial.cr @@ -9,11 +9,15 @@ module Spectator::Expectations end def to(matcher : Matchers::ValueMatcher(ExpectedType)) : Nil forall ExpectedType - raise NotImplementedError.new("ValueExpectationPartial#to") + expectation = ValueExpectation.new(self, matcher) + result = expectation.eval + ExpectationRegistry.current.report(result) end def to_not(matcher : Matchers::ValueMatcher(ExpectedType)) : Nil forall ExpectedType - raise NotImplementedError.new("ValueExpectationPartial#to_not") + expectation = ValueExpectation.new(self, matcher) + result = expectation.eval(true) + ExpectationRegistry.current.report(result) end # ditto diff --git a/src/spectator/failed_result.cr b/src/spectator/failed_result.cr index 1cc0826..20cf7d3 100644 --- a/src/spectator/failed_result.cr +++ b/src/spectator/failed_result.cr @@ -3,6 +3,11 @@ require "./result" module Spectator class FailedResult < Result getter error : Exception + getter expectations : Expectations::ExpectationResults + + def initialize(example, elapsed, @expectations, @error) + super(example, elapsed) + end def passed? false @@ -19,9 +24,5 @@ module Spectator def pending? false end - - def initialize(example, elapsed, @error) - super(example, elapsed) - end end end diff --git a/src/spectator/matchers/equality_matcher.cr b/src/spectator/matchers/equality_matcher.cr index 033da80..ad9562b 100644 --- a/src/spectator/matchers/equality_matcher.cr +++ b/src/spectator/matchers/equality_matcher.cr @@ -2,15 +2,15 @@ require "./value_matcher" module Spectator::Matchers class EqualityMatcher(ExpectedType) < ValueMatcher(ExpectedType) - def match?(partial : ValueExpectationPartial(ActualType)) : Bool forall ActualType + def match?(partial : Expectations::ValueExpectationPartial(ActualType)) : Bool forall ActualType partial.actual == expected end - def message(partial : ValueExpectationPartial(ActualType)) : String forall ActualType + def message(partial : Expectations::ValueExpectationPartial(ActualType)) : String forall ActualType "Expected #{partial.label} to equal #{label} (using ==)" end - def negated_message(partial : ValueExpectationPartial(ActualType)) : String forall ActualType + def negated_message(partial : Expectations::ValueExpectationPartial(ActualType)) : String forall ActualType "Expected #{partial.label} to not equal #{label} (using ==)" end end diff --git a/src/spectator/runnable_example.cr b/src/spectator/runnable_example.cr index af49c82..354ebba 100644 --- a/src/spectator/runnable_example.cr +++ b/src/spectator/runnable_example.cr @@ -3,6 +3,7 @@ require "./example" module Spectator abstract class RunnableExample < Example def run + Expectations::ExpectationRegistry.start(self) result = ResultCapture.new group.run_before_all_hooks group.run_before_each_hooks @@ -13,7 +14,8 @@ module Spectator group.run_after_each_hooks group.run_after_all_hooks end - translate_result(result) + expectations = Expectations::ExpectationRegistry.finish + translate_result(result, expectations) end private def wrapped_capture_result(result) @@ -32,14 +34,14 @@ module Spectator end end - private def translate_result(result) + private def translate_result(result, expectations) case (error = result.error) when Nil - SuccessfulResult.new(self, result.elapsed) + SuccessfulResult.new(self, result.elapsed, expectations) when ExpectationFailed - FailedResult.new(self, result.elapsed, error) + FailedResult.new(self, result.elapsed, expectations, error) else - ErroredResult.new(self, result.elapsed, error) + ErroredResult.new(self, result.elapsed, expectations, error) end end diff --git a/src/spectator/successful_result.cr b/src/spectator/successful_result.cr index 3a9681d..714430d 100644 --- a/src/spectator/successful_result.cr +++ b/src/spectator/successful_result.cr @@ -2,6 +2,12 @@ require "./result" module Spectator class SuccessfulResult < Result + getter expectations : Expectations::ExpectationResults + + def initialize(example, elapsed, @expectations) + super(example, elapsed) + end + def passed? true end