2021-01-16 05:32:02 +00:00
|
|
|
require "../block"
|
2021-06-10 03:57:17 +00:00
|
|
|
require "../example_pending"
|
2021-01-16 17:22:23 +00:00
|
|
|
require "../expectation"
|
2021-01-21 04:38:34 +00:00
|
|
|
require "../expectation_failed"
|
2021-02-13 05:46:22 +00:00
|
|
|
require "../location"
|
2021-06-12 00:59:10 +00:00
|
|
|
require "../pending_result"
|
2021-01-16 05:32:02 +00:00
|
|
|
require "../value"
|
2019-01-23 23:42:17 +00:00
|
|
|
|
2021-01-10 02:50:32 +00:00
|
|
|
module Spectator::DSL
|
|
|
|
# Methods and macros for asserting that conditions are met.
|
2021-01-16 17:22:23 +00:00
|
|
|
module Expectations
|
2021-01-10 18:09:28 +00:00
|
|
|
# Immediately fail the current test.
|
|
|
|
# A reason can be specified with *message*.
|
|
|
|
def fail(message = "Example failed", *, _file = __FILE__, _line = __LINE__)
|
2021-07-10 09:32:55 +00:00
|
|
|
raise ExampleFailed.new(Location.new(_file, _line), message)
|
2021-01-10 18:09:28 +00:00
|
|
|
end
|
|
|
|
|
2021-06-10 03:57:17 +00:00
|
|
|
# Mark the current test as pending and immediately abort.
|
|
|
|
# A reason can be specified with *message*.
|
2021-07-17 22:25:32 +00:00
|
|
|
def pending(message = PendingResult::DEFAULT_REASON, *, _file = __FILE__, _line = __LINE__)
|
|
|
|
raise ExamplePending.new(Location.new(_file, _line), message)
|
2021-06-10 03:57:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Mark the current test as skipped and immediately abort.
|
|
|
|
# A reason can be specified with *message*.
|
2021-07-17 22:25:32 +00:00
|
|
|
def skip(message = PendingResult::DEFAULT_REASON, *, _file = __FILE__, _line = __LINE__)
|
|
|
|
raise ExamplePending.new(Location.new(_file, _line), message)
|
2021-06-10 03:57:17 +00:00
|
|
|
end
|
|
|
|
|
2021-01-10 18:09:28 +00:00
|
|
|
# Starts an expectation.
|
|
|
|
# This should be followed up with `Assertion::Target#to` or `Assertion::Target#to_not`.
|
|
|
|
# The value passed in will be checked to see if it satisfies the conditions of the specified matcher.
|
|
|
|
#
|
|
|
|
# This macro should be used like so:
|
|
|
|
# ```
|
|
|
|
# expect(actual).to eq(expected)
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# Where the actual value is returned by the system under test,
|
|
|
|
# and the expected value is what the actual value should be to satisfy the condition.
|
2021-01-10 02:50:32 +00:00
|
|
|
macro expect(actual)
|
|
|
|
%actual = begin
|
|
|
|
{{actual}}
|
|
|
|
end
|
2019-01-09 18:28:31 +00:00
|
|
|
|
2021-01-16 05:32:02 +00:00
|
|
|
%expression = ::Spectator::Value.new(%actual, {{actual.stringify}})
|
2021-02-13 05:46:22 +00:00
|
|
|
%location = ::Spectator::Location.new({{actual.filename}}, {{actual.line_number}})
|
|
|
|
::Spectator::Expectation::Target.new(%expression, %location)
|
2018-11-03 02:48:36 +00:00
|
|
|
end
|
2019-01-09 18:28:31 +00:00
|
|
|
|
2021-01-10 18:09:28 +00:00
|
|
|
# Starts an expectation.
|
|
|
|
# This should be followed up with `Assertion::Target#to` or `Assertion::Target#not_to`.
|
|
|
|
# The value passed in will be checked to see if it satisfies the conditions of the specified matcher.
|
|
|
|
#
|
|
|
|
# This macro should be used like so:
|
|
|
|
# ```
|
|
|
|
# expect { raise "foo" }.to raise_error
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# The block of code is passed along for validation to the matchers.
|
|
|
|
#
|
|
|
|
# The short, one argument syntax used for passing methods to blocks can be used.
|
|
|
|
# So instead of doing this:
|
|
|
|
# ```
|
|
|
|
# expect(subject.size).to eq(5)
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# The following syntax can be used instead:
|
|
|
|
# ```
|
|
|
|
# expect(&.size).to eq(5)
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# The method passed will always be evaluated on the subject.
|
|
|
|
#
|
|
|
|
# TECHNICAL NOTE:
|
|
|
|
# This macro uses an ugly hack to detect the short-hand syntax.
|
|
|
|
#
|
2021-01-10 02:50:32 +00:00
|
|
|
# The Crystal compiler will translate:
|
|
|
|
# ```
|
|
|
|
# &.foo
|
|
|
|
# ```
|
2021-01-10 18:09:28 +00:00
|
|
|
#
|
|
|
|
# effectively to:
|
2021-01-10 02:50:32 +00:00
|
|
|
# ```
|
|
|
|
# { |__arg0| __arg0.foo }
|
|
|
|
# ```
|
2021-01-10 18:09:28 +00:00
|
|
|
macro expect(&block)
|
|
|
|
{% if block.args.size == 1 && block.args[0] =~ /^__arg\d+$/ && block.body.is_a?(Call) && block.body.id =~ /^__arg\d+\./ %}
|
|
|
|
{% method_name = block.body.id.split('.')[1..-1].join('.') %}
|
|
|
|
%block = ::Spectator::Block.new({{"#" + method_name}}) do
|
|
|
|
subject.{{method_name.id}}
|
|
|
|
end
|
|
|
|
{% elsif block.args.empty? %}
|
|
|
|
%block = ::Spectator::Block.new({{"`" + block.body.stringify + "`"}}) {{block}}
|
|
|
|
{% else %}
|
|
|
|
{% raise "Unexpected block arguments in 'expect' call" %}
|
|
|
|
{% end %}
|
|
|
|
|
2021-02-13 05:46:22 +00:00
|
|
|
%location = ::Spectator::Location.new({{block.filename}}, {{block.line_number}})
|
|
|
|
::Spectator::Expectation::Target.new(%block, %location)
|
2021-01-10 18:09:28 +00:00
|
|
|
end
|
2021-01-10 02:50:32 +00:00
|
|
|
|
2021-01-10 18:09:28 +00:00
|
|
|
# Short-hand for expecting something of the subject.
|
|
|
|
#
|
|
|
|
# These two are functionally equivalent:
|
|
|
|
# ```
|
|
|
|
# expect(subject).to eq("foo")
|
|
|
|
# is_expected.to eq("foo")
|
|
|
|
# ```
|
|
|
|
macro is_expected
|
|
|
|
expect(subject)
|
|
|
|
end
|
2021-01-10 02:50:32 +00:00
|
|
|
|
2021-01-10 18:09:28 +00:00
|
|
|
# Short-hand form of `#is_expected` that can be used for one-liner syntax.
|
|
|
|
#
|
|
|
|
# For instance:
|
|
|
|
# ```
|
|
|
|
# it "is 42" do
|
|
|
|
# expect(subject).to eq(42)
|
|
|
|
# end
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# Can be shortened to:
|
|
|
|
# ```
|
|
|
|
# it { is(42) }
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# These three are functionally equivalent:
|
|
|
|
# ```
|
|
|
|
# expect(subject).to eq("foo")
|
|
|
|
# is_expected.to eq("foo")
|
|
|
|
# is("foo")
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# See also: `#is_not`
|
|
|
|
macro is(expected)
|
|
|
|
expect(subject).to(eq({{expected}}))
|
|
|
|
end
|
2021-01-10 02:50:32 +00:00
|
|
|
|
2021-01-10 18:09:28 +00:00
|
|
|
# Short-hand, negated form of `#is_expected` that can be used for one-liner syntax.
|
|
|
|
#
|
|
|
|
# For instance:
|
|
|
|
# ```
|
|
|
|
# it "is not 42" do
|
|
|
|
# expect(subject).to_not eq(42)
|
|
|
|
# end
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# Can be shortened to:
|
|
|
|
# ```
|
|
|
|
# it { is_not(42) }
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# These three are functionally equivalent:
|
|
|
|
# ```
|
|
|
|
# expect(subject).not_to eq("foo")
|
|
|
|
# is_expected.not_to eq("foo")
|
|
|
|
# is_not("foo")
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# See also: `#is`
|
|
|
|
macro is_not(expected)
|
|
|
|
expect(subject).not_to(eq({{expected}}))
|
|
|
|
end
|
2018-09-15 17:21:23 +00:00
|
|
|
end
|
|
|
|
end
|