1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
module RSpec
module Expectations
# Wraps the target of an expectation.
#
# @example
# expect(something) # => ExpectationTarget wrapping something
# expect { do_something } # => ExpectationTarget wrapping the block
#
# # used with `to`
# expect(actual).to eq(3)
#
# # with `not_to`
# expect(actual).not_to eq(3)
#
# @note `ExpectationTarget` is not intended to be instantiated
# directly by users. Use `expect` instead.
class ExpectationTarget
# @private
# Used as a sentinel value to be able to tell when the user
# did not pass an argument. We can't use `nil` for that because
# `nil` is a valid value to pass.
UndefinedValue = Module.new
# @note this name aligns with `Minitest::Expectation` so that our
# {InstanceMethods} module can be included in that class when
# used in a Minitest context.
# @return [Object] the target of the expectation
attr_reader :target
# @api private
def initialize(value)
@target = value
end
# @private
def self.for(value, block)
if UndefinedValue.equal?(value)
unless block
raise ArgumentError, "You must pass either an argument or a block to `expect`."
end
BlockExpectationTarget.new(block)
elsif block
raise ArgumentError, "You cannot pass both an argument and a block to `expect`."
else
ValueExpectationTarget.new(value)
end
end
# Defines instance {ExpectationTarget} instance methods. These are defined
# in a module so we can include it in `Minitest::Expectation` when
# `rspec/expectations/minitest_integration` is loaded in order to
# support usage with Minitest.
module InstanceMethods
# Runs the given expectation, passing if `matcher` returns true.
# @example
# expect(value).to eq(5)
# expect { perform }.to raise_error
# @param [Matcher]
# matcher
# @param [String, Proc] message optional message to display when the expectation fails
# @return [Boolean] true if the expectation succeeds (else raises)
# @see RSpec::Matchers
def to(matcher=nil, message=nil, &block)
prevent_operator_matchers(:to) unless matcher
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(target, matcher, message, &block)
end
# Runs the given expectation, passing if `matcher` returns false.
# @example
# expect(value).not_to eq(5)
# @param [Matcher]
# matcher
# @param [String, Proc] message optional message to display when the expectation fails
# @return [Boolean] false if the negative expectation succeeds (else raises)
# @see RSpec::Matchers
def not_to(matcher=nil, message=nil, &block)
prevent_operator_matchers(:not_to) unless matcher
RSpec::Expectations::NegativeExpectationHandler.handle_matcher(target, matcher, message, &block)
end
alias to_not not_to
private
def prevent_operator_matchers(verb)
raise ArgumentError, "The expect syntax does not support operator matchers, " \
"so you must pass a matcher to `##{verb}`."
end
end
include InstanceMethods
end
# @private
# Validates the provided matcher to ensure it supports block
# expectations, in order to avoid user confusion when they
# use a block thinking the expectation will be on the return
# value of the block rather than the block itself.
class ValueExpectationTarget < ExpectationTarget
def to(matcher=nil, message=nil, &block)
enforce_value_expectation(matcher)
super
end
def not_to(matcher=nil, message=nil, &block)
enforce_value_expectation(matcher)
super
end
private
def enforce_value_expectation(matcher)
return if supports_value_expectations?(matcher)
RSpec.deprecate(
"expect(value).to #{RSpec::Support::ObjectFormatter.format(matcher)}",
:message =>
"The implicit block expectation syntax is deprecated, you should pass " \
"a block rather than an argument to `expect` to use the provided " \
"block expectation matcher or the matcher must implement " \
"`supports_value_expectations?`. e.g `expect { value }.to " \
"#{RSpec::Support::ObjectFormatter.format(matcher)}` not " \
"`expect(value).to #{RSpec::Support::ObjectFormatter.format(matcher)}`"
)
end
def supports_value_expectations?(matcher)
!matcher.respond_to?(:supports_value_expectations?) || matcher.supports_value_expectations?
end
end
# @private
# Validates the provided matcher to ensure it supports block
# expectations, in order to avoid user confusion when they
# use a block thinking the expectation will be on the return
# value of the block rather than the block itself.
class BlockExpectationTarget < ExpectationTarget
def to(matcher, message=nil, &block)
enforce_block_expectation(matcher)
super
end
def not_to(matcher, message=nil, &block)
enforce_block_expectation(matcher)
super
end
alias to_not not_to
private
def enforce_block_expectation(matcher)
return if supports_block_expectations?(matcher)
raise ExpectationNotMetError, "You must pass an argument rather than a block to `expect` to use the provided " \
"matcher (#{RSpec::Support::ObjectFormatter.format(matcher)}), or the matcher must implement " \
"`supports_block_expectations?`."
end
def supports_block_expectations?(matcher)
matcher.respond_to?(:supports_block_expectations?) && matcher.supports_block_expectations?
end
end
end
end
|