Merge branch 'release/0.9' into dev/mocks-and-doubles

This commit is contained in:
Michael Miller 2019-11-14 18:47:25 -07:00
commit 28ec1062e8
4 changed files with 59 additions and 5 deletions

View file

@ -45,6 +45,24 @@ module Spectator::Expectations
to_not(matcher) to_not(matcher)
end end
# Asserts that some criteria defined by the matcher is eventually satisfied.
# The expectation is checked after the example finishes and all hooks have run.
def to_eventually(matcher) : Nil
Harness.current.defer { to(matcher) }
end
# Asserts that some criteria defined by the matcher is never satisfied.
# The expectation is checked after the example finishes and all hooks have run.
def to_never(matcher) : Nil
Harness.current.defer { to_not(matcher) }
end
# ditto
@[AlwaysInline]
def never_to(matcher) : Nil
to_never(matcher)
end
# Reports an expectation to the current harness. # Reports an expectation to the current harness.
private def report(match_data : Matchers::MatchData) private def report(match_data : Matchers::MatchData)
expectation = Expectation.new(match_data, @source) expectation = Expectation.new(match_data, @source)

View file

@ -55,11 +55,23 @@ module Spectator
@reporter.expectations @reporter.expectations
end end
# Marks a block of code to run later.
def defer(&block : ->) : Nil
@deferred << block
end
# Runs all deferred blocks.
def run_deferred : Nil
@deferred.each(&.call)
@deferred.clear
end
# Creates a new harness. # Creates a new harness.
# The example the harness is for should be passed in. # The example the harness is for should be passed in.
private def initialize(@example) private def initialize(@example)
@reporter = Expectations::ExpectationReporter.new @reporter = Expectations::ExpectationReporter.new
@mocks = Mocks::Registry.new(@example.group.context) @mocks = Mocks::Registry.new(@example.group.context)
@deferred = Deque(->).new
end end
end end
end end

View file

@ -20,6 +20,7 @@ module Spectator
context.run_before_hooks(self) context.run_before_hooks(self)
run_example(result) run_example(result)
context.run_after_hooks(self) context.run_after_hooks(self)
run_deferred(result)
end end
end end
@ -40,6 +41,17 @@ module Spectator
end end
end end
# Runs the deferred blocks of code and captures the result.
private def run_deferred(result)
result.elapsed += Time.measure do
begin
Harness.current.run_deferred
rescue ex # Catch all errors and handle them later.
result.error = ex
end
end
end
# Creates a result instance from captured result information. # Creates a result instance from captured result information.
private def translate_result(result, expectations) private def translate_result(result, expectations)
case (error = result.error) case (error = result.error)

View file

@ -16,7 +16,7 @@ class Object
# ``` # ```
# require "spectator/should" # require "spectator/should"
# ``` # ```
def should(matcher : ::Spectator::Matchers::Matcher) def should(matcher)
# First argument of the `Expectation` initializer is the expression label. # First argument of the `Expectation` initializer is the expression label.
# However, since this isn't a macro and we can't "look behind" this method call # However, since this isn't a macro and we can't "look behind" this method call
# to see what it was invoked on, the argument is an empty string. # to see what it was invoked on, the argument is an empty string.
@ -28,17 +28,29 @@ class Object
# Works the same as `#should` except the condition is inverted. # Works the same as `#should` except the condition is inverted.
# When `#should` succeeds, this method will fail, and vice-versa. # When `#should` succeeds, this method will fail, and vice-versa.
def should_not(matcher : ::Spectator::Matchers::Matcher) def should_not(matcher)
actual = ::Spectator::TestValue.new(self) actual = ::Spectator::TestValue.new(self)
source = ::Spectator::Source.new(__FILE__, __LINE__) source = ::Spectator::Source.new(__FILE__, __LINE__)
::Spectator::Expectations::ExpectationPartial.new(actual, source).to_not(matcher) ::Spectator::Expectations::ExpectationPartial.new(actual, source).to_not(matcher)
end end
# Works the same as `#should` except that the condition check is postphoned.
# The expectation is checked after the example finishes and all hooks have run.
def should_eventually(matcher)
::Spectator::Harness.current.defer { should(matcher) }
end
# Works the same as `#should_not` except that the condition check is postphoned.
# The expectation is checked after the example finishes and all hooks have run.
def should_never(matcher)
::Spectator::Harness.current.defer { should_not(matcher) }
end
end end
struct Proc(*T, R) struct Proc(*T, R)
# Extension method to create an expectation for a block of code (proc). # Extension method to create an expectation for a block of code (proc).
# Depending on the matcher, the proc may be executed multiple times. # Depending on the matcher, the proc may be executed multiple times.
def should(matcher : ::Spectator::Matchers::Matcher) def should(matcher)
actual = ::Spectator::TestBlock.new(self) actual = ::Spectator::TestBlock.new(self)
source = ::Spectator::Source.new(__FILE__, __LINE__) source = ::Spectator::Source.new(__FILE__, __LINE__)
::Spectator::Expectations::ExpectationPartial.new(actual, source).to(matcher) ::Spectator::Expectations::ExpectationPartial.new(actual, source).to(matcher)
@ -46,7 +58,7 @@ struct Proc(*T, R)
# Works the same as `#should` except the condition is inverted. # Works the same as `#should` except the condition is inverted.
# When `#should` succeeds, this method will fail, and vice-versa. # When `#should` succeeds, this method will fail, and vice-versa.
def should_not(matcher : ::Spectator::Matchers::Matcher) def should_not(matcher)
actual = ::Spectator::TestBlock.new(self) actual = ::Spectator::TestBlock.new(self)
source = ::Spectator::Source.new(__FILE__, __LINE__) source = ::Spectator::Source.new(__FILE__, __LINE__)
::Spectator::Expectations::BlockExpectationPartial.new(actual, source).to_not(matcher) ::Spectator::Expectations::BlockExpectationPartial.new(actual, source).to_not(matcher)