rspec-expectations 3.0.4 → 3.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +1 -1
- data/Changelog.md +530 -5
- data/{License.txt → LICENSE.md} +5 -4
- data/README.md +73 -31
- data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
- data/lib/rspec/expectations/configuration.rb +96 -1
- data/lib/rspec/expectations/expectation_target.rb +82 -38
- data/lib/rspec/expectations/fail_with.rb +11 -6
- data/lib/rspec/expectations/failure_aggregator.rb +229 -0
- data/lib/rspec/expectations/handler.rb +36 -15
- data/lib/rspec/expectations/minitest_integration.rb +43 -2
- data/lib/rspec/expectations/syntax.rb +5 -5
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/expectations.rb +15 -1
- data/lib/rspec/matchers/aliased_matcher.rb +79 -4
- data/lib/rspec/matchers/built_in/all.rb +11 -0
- data/lib/rspec/matchers/built_in/base_matcher.rb +111 -28
- data/lib/rspec/matchers/built_in/be.rb +28 -114
- data/lib/rspec/matchers/built_in/be_between.rb +1 -1
- data/lib/rspec/matchers/built_in/be_instance_of.rb +5 -1
- data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -1
- data/lib/rspec/matchers/built_in/be_within.rb +5 -12
- data/lib/rspec/matchers/built_in/change.rb +171 -63
- data/lib/rspec/matchers/built_in/compound.rb +201 -30
- data/lib/rspec/matchers/built_in/contain_exactly.rb +73 -12
- data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
- data/lib/rspec/matchers/built_in/eq.rb +3 -38
- data/lib/rspec/matchers/built_in/eql.rb +2 -2
- data/lib/rspec/matchers/built_in/equal.rb +3 -3
- data/lib/rspec/matchers/built_in/exist.rb +7 -3
- data/lib/rspec/matchers/built_in/has.rb +93 -30
- data/lib/rspec/matchers/built_in/have_attributes.rb +114 -0
- data/lib/rspec/matchers/built_in/include.rb +133 -25
- data/lib/rspec/matchers/built_in/match.rb +79 -2
- data/lib/rspec/matchers/built_in/operators.rb +14 -5
- data/lib/rspec/matchers/built_in/output.rb +59 -2
- data/lib/rspec/matchers/built_in/raise_error.rb +130 -27
- data/lib/rspec/matchers/built_in/respond_to.rb +117 -15
- data/lib/rspec/matchers/built_in/satisfy.rb +28 -14
- data/lib/rspec/matchers/built_in/{start_and_end_with.rb → start_or_end_with.rb} +20 -8
- data/lib/rspec/matchers/built_in/throw_symbol.rb +15 -5
- data/lib/rspec/matchers/built_in/yield.rb +129 -156
- data/lib/rspec/matchers/built_in.rb +5 -3
- data/lib/rspec/matchers/composable.rb +24 -36
- data/lib/rspec/matchers/dsl.rb +203 -37
- data/lib/rspec/matchers/english_phrasing.rb +58 -0
- data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +82 -0
- data/lib/rspec/matchers/fail_matchers.rb +42 -0
- data/lib/rspec/matchers/generated_descriptions.rb +1 -2
- data/lib/rspec/matchers/matcher_delegator.rb +3 -4
- data/lib/rspec/matchers/matcher_protocol.rb +105 -0
- data/lib/rspec/matchers.rb +267 -144
- data.tar.gz.sig +0 -0
- metadata +71 -49
- metadata.gz.sig +0 -0
- data/lib/rspec/matchers/pretty.rb +0 -77
@@ -21,6 +21,12 @@ module RSpec
|
|
21
21
|
# `nil` is a valid value to pass.
|
22
22
|
UndefinedValue = Module.new
|
23
23
|
|
24
|
+
# @note this name aligns with `Minitest::Expectation` so that our
|
25
|
+
# {InstanceMethods} module can be included in that class when
|
26
|
+
# used in a Minitest context.
|
27
|
+
# @return [Object] the target of the expectation
|
28
|
+
attr_reader :target
|
29
|
+
|
24
30
|
# @api private
|
25
31
|
def initialize(value)
|
26
32
|
@target = value
|
@@ -36,43 +42,89 @@ module RSpec
|
|
36
42
|
elsif block
|
37
43
|
raise ArgumentError, "You cannot pass both an argument and a block to `expect`."
|
38
44
|
else
|
39
|
-
new(value)
|
45
|
+
ValueExpectationTarget.new(value)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Defines instance {ExpectationTarget} instance methods. These are defined
|
50
|
+
# in a module so we can include it in `Minitest::Expectation` when
|
51
|
+
# `rspec/expectations/minitest_integration` is loaded in order to
|
52
|
+
# support usage with Minitest.
|
53
|
+
module InstanceMethods
|
54
|
+
# Runs the given expectation, passing if `matcher` returns true.
|
55
|
+
# @example
|
56
|
+
# expect(value).to eq(5)
|
57
|
+
# expect { perform }.to raise_error
|
58
|
+
# @param [Matcher]
|
59
|
+
# matcher
|
60
|
+
# @param [String, Proc] message optional message to display when the expectation fails
|
61
|
+
# @return [Boolean] true if the expectation succeeds (else raises)
|
62
|
+
# @see RSpec::Matchers
|
63
|
+
def to(matcher=nil, message=nil, &block)
|
64
|
+
prevent_operator_matchers(:to) unless matcher
|
65
|
+
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(target, matcher, message, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Runs the given expectation, passing if `matcher` returns false.
|
69
|
+
# @example
|
70
|
+
# expect(value).not_to eq(5)
|
71
|
+
# @param [Matcher]
|
72
|
+
# matcher
|
73
|
+
# @param [String, Proc] message optional message to display when the expectation fails
|
74
|
+
# @return [Boolean] false if the negative expectation succeeds (else raises)
|
75
|
+
# @see RSpec::Matchers
|
76
|
+
def not_to(matcher=nil, message=nil, &block)
|
77
|
+
prevent_operator_matchers(:not_to) unless matcher
|
78
|
+
RSpec::Expectations::NegativeExpectationHandler.handle_matcher(target, matcher, message, &block)
|
79
|
+
end
|
80
|
+
alias to_not not_to
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def prevent_operator_matchers(verb)
|
85
|
+
raise ArgumentError, "The expect syntax does not support operator matchers, " \
|
86
|
+
"so you must pass a matcher to `##{verb}`."
|
40
87
|
end
|
41
88
|
end
|
42
89
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
90
|
+
include InstanceMethods
|
91
|
+
end
|
92
|
+
|
93
|
+
# @private
|
94
|
+
# Validates the provided matcher to ensure it supports block
|
95
|
+
# expectations, in order to avoid user confusion when they
|
96
|
+
# use a block thinking the expectation will be on the return
|
97
|
+
# value of the block rather than the block itself.
|
98
|
+
class ValueExpectationTarget < ExpectationTarget
|
52
99
|
def to(matcher=nil, message=nil, &block)
|
53
|
-
|
54
|
-
|
100
|
+
enforce_value_expectation(matcher)
|
101
|
+
super
|
55
102
|
end
|
56
103
|
|
57
|
-
# Runs the given expectation, passing if `matcher` returns false.
|
58
|
-
# @example
|
59
|
-
# expect(value).not_to eq(5)
|
60
|
-
# @param [Matcher]
|
61
|
-
# matcher
|
62
|
-
# @param [String or Proc] message optional message to display when the expectation fails
|
63
|
-
# @return [Boolean] false if the negative expectation succeeds (else raises)
|
64
|
-
# @see RSpec::Matchers
|
65
104
|
def not_to(matcher=nil, message=nil, &block)
|
66
|
-
|
67
|
-
|
105
|
+
enforce_value_expectation(matcher)
|
106
|
+
super
|
68
107
|
end
|
69
|
-
alias to_not not_to
|
70
108
|
|
71
109
|
private
|
72
110
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
111
|
+
def enforce_value_expectation(matcher)
|
112
|
+
return if supports_value_expectations?(matcher)
|
113
|
+
|
114
|
+
RSpec.deprecate(
|
115
|
+
"expect(value).to #{RSpec::Support::ObjectFormatter.format(matcher)}",
|
116
|
+
:message =>
|
117
|
+
"The implicit block expectation syntax is deprecated, you should pass " \
|
118
|
+
"a block rather than an argument to `expect` to use the provided " \
|
119
|
+
"block expectation matcher or the matcher must implement " \
|
120
|
+
"`supports_value_expectations?`. e.g `expect { value }.to " \
|
121
|
+
"#{RSpec::Support::ObjectFormatter.format(matcher)}` not " \
|
122
|
+
"`expect(value).to #{RSpec::Support::ObjectFormatter.format(matcher)}`"
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
def supports_value_expectations?(matcher)
|
127
|
+
!matcher.respond_to?(:supports_value_expectations?) || matcher.supports_value_expectations?
|
76
128
|
end
|
77
129
|
end
|
78
130
|
|
@@ -98,21 +150,13 @@ module RSpec
|
|
98
150
|
def enforce_block_expectation(matcher)
|
99
151
|
return if supports_block_expectations?(matcher)
|
100
152
|
|
101
|
-
raise ExpectationNotMetError, "You must pass an argument rather than " \
|
102
|
-
"
|
103
|
-
"
|
153
|
+
raise ExpectationNotMetError, "You must pass an argument rather than a block to `expect` to use the provided " \
|
154
|
+
"matcher (#{RSpec::Support::ObjectFormatter.format(matcher)}), or the matcher must implement " \
|
155
|
+
"`supports_block_expectations?`."
|
104
156
|
end
|
105
157
|
|
106
158
|
def supports_block_expectations?(matcher)
|
107
|
-
matcher.supports_block_expectations?
|
108
|
-
rescue NoMethodError
|
109
|
-
false
|
110
|
-
end
|
111
|
-
|
112
|
-
def description_of(matcher)
|
113
|
-
matcher.description
|
114
|
-
rescue NoMethodError
|
115
|
-
matcher.inspect
|
159
|
+
matcher.respond_to?(:supports_block_expectations?) && matcher.supports_block_expectations?
|
116
160
|
end
|
117
161
|
end
|
118
162
|
end
|
@@ -1,12 +1,18 @@
|
|
1
|
-
RSpec::Support.require_rspec_support 'differ'
|
2
|
-
|
3
1
|
module RSpec
|
4
2
|
module Expectations
|
5
3
|
class << self
|
4
|
+
# @private
|
5
|
+
class Differ
|
6
|
+
# @private
|
7
|
+
OBJECT_PREPARER = lambda do |object|
|
8
|
+
RSpec::Matchers::Composable.surface_descriptions_in(object)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
6
12
|
# @private
|
7
13
|
def differ
|
8
14
|
RSpec::Support::Differ.new(
|
9
|
-
:object_preparer =>
|
15
|
+
:object_preparer => Differ::OBJECT_PREPARER,
|
10
16
|
:color => RSpec::Matchers.configuration.color?
|
11
17
|
)
|
12
18
|
end
|
@@ -24,10 +30,9 @@ module RSpec
|
|
24
30
|
"appropriate failure_message[_when_negated] method to return a string?"
|
25
31
|
end
|
26
32
|
|
27
|
-
|
28
|
-
message = "#{message}\nDiff:#{diff}" unless diff.empty?
|
33
|
+
message = ::RSpec::Matchers::ExpectedsForMultipleDiffs.from(expected).message_with_diff(message, differ, actual)
|
29
34
|
|
30
|
-
|
35
|
+
RSpec::Support.notify_failure(RSpec::Expectations::ExpectationNotMetError.new message)
|
31
36
|
end
|
32
37
|
end
|
33
38
|
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Expectations
|
3
|
+
# @private
|
4
|
+
class FailureAggregator
|
5
|
+
attr_reader :block_label, :metadata
|
6
|
+
|
7
|
+
# @private
|
8
|
+
class AggregatedFailure
|
9
|
+
# @private
|
10
|
+
MESSAGE =
|
11
|
+
'AggregatedFailure: This method caused a failure which has been ' \
|
12
|
+
'suppressed to be aggregated into our failure report by returning ' \
|
13
|
+
'this value, further errors can be ignored.'
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
MESSAGE
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
AGGREGATED_FAILURE = AggregatedFailure.new
|
21
|
+
|
22
|
+
def aggregate
|
23
|
+
RSpec::Support.with_failure_notifier(self) do
|
24
|
+
begin
|
25
|
+
yield
|
26
|
+
rescue ExpectationNotMetError => e
|
27
|
+
# Normally, expectation failures will be notified via the `call` method, below,
|
28
|
+
# but since the failure notifier uses a thread local variable, failing expectations
|
29
|
+
# in another thread will still raise. We handle that here and categorize it as part
|
30
|
+
# of `failures` rather than letting it fall through and be categorized as part of
|
31
|
+
# `other_errors`.
|
32
|
+
failures << e
|
33
|
+
rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
|
34
|
+
# While it is normally a bad practice to rescue `Exception`, it's important we do
|
35
|
+
# so here. It's low risk (`notify_aggregated_failures` below will re-raise the exception,
|
36
|
+
# or raise a `MultipleExpectationsNotMetError` that includes the exception), and it's
|
37
|
+
# essential that the user is notified of expectation failures that may have already
|
38
|
+
# occurred in the `aggregate_failures` block. Those expectation failures may provide
|
39
|
+
# important diagnostics for understanding why this exception occurred, and if we simply
|
40
|
+
# allowed this exception to be raised as-is, it would (wrongly) suggest to the user
|
41
|
+
# that the expectation passed when it did not, which would be quite confusing.
|
42
|
+
other_errors << e
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
notify_aggregated_failures
|
47
|
+
end
|
48
|
+
|
49
|
+
def failures
|
50
|
+
@failures ||= []
|
51
|
+
end
|
52
|
+
|
53
|
+
def other_errors
|
54
|
+
@other_errors ||= []
|
55
|
+
end
|
56
|
+
|
57
|
+
# This method is defined to satisfy the callable interface
|
58
|
+
# expected by `RSpec::Support.with_failure_notifier`.
|
59
|
+
def call(failure, options)
|
60
|
+
source_id = options[:source_id]
|
61
|
+
return if source_id && @seen_source_ids.key?(source_id)
|
62
|
+
|
63
|
+
@seen_source_ids[source_id] = true
|
64
|
+
assign_backtrace(failure) unless failure.backtrace
|
65
|
+
failures << failure
|
66
|
+
|
67
|
+
AGGREGATED_FAILURE
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
if RSpec::Support::Ruby.jruby? && RSpec::Support::Ruby.jruby_version < '9.2.0.0'
|
73
|
+
# On JRuby 9.1.x.x and before, `caller` and `raise` produce different backtraces with
|
74
|
+
# regards to `.java` stack frames. It's important that we use `raise` for JRuby to produce
|
75
|
+
# a backtrace that has a continuous common section with the raised `MultipleExpectationsNotMetError`,
|
76
|
+
# so that rspec-core's truncation logic can work properly on it to list the backtrace
|
77
|
+
# relative to the `aggregate_failures` block.
|
78
|
+
def assign_backtrace(failure)
|
79
|
+
raise failure
|
80
|
+
rescue failure.class => e
|
81
|
+
failure.set_backtrace(e.backtrace)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
# Using `caller` performs better (and is simpler) than `raise` on most Rubies.
|
85
|
+
def assign_backtrace(failure)
|
86
|
+
failure.set_backtrace(caller)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def initialize(block_label, metadata)
|
91
|
+
@block_label = block_label
|
92
|
+
@metadata = metadata
|
93
|
+
@seen_source_ids = {} # don't want to load stdlib set
|
94
|
+
end
|
95
|
+
|
96
|
+
def notify_aggregated_failures
|
97
|
+
all_errors = failures + other_errors
|
98
|
+
|
99
|
+
case all_errors.size
|
100
|
+
when 0 then return true
|
101
|
+
when 1 then RSpec::Support.notify_failure all_errors.first
|
102
|
+
else RSpec::Support.notify_failure MultipleExpectationsNotMetError.new(self)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Exception raised from `aggregate_failures` when multiple expectations fail.
|
108
|
+
class MultipleExpectationsNotMetError
|
109
|
+
# @return [String] The fully formatted exception message.
|
110
|
+
def message
|
111
|
+
@message ||= (["#{summary}:"] + enumerated_failures + enumerated_errors).join("\n\n")
|
112
|
+
end
|
113
|
+
|
114
|
+
# @return [Array<RSpec::Expectations::ExpectationNotMetError>] The list of expectation failures.
|
115
|
+
def failures
|
116
|
+
@failure_aggregator.failures
|
117
|
+
end
|
118
|
+
|
119
|
+
# @return [Array<Exception>] The list of other exceptions.
|
120
|
+
def other_errors
|
121
|
+
@failure_aggregator.other_errors
|
122
|
+
end
|
123
|
+
|
124
|
+
# @return [Array<Exception>] The list of expectation failures and other exceptions, combined.
|
125
|
+
attr_reader :all_exceptions
|
126
|
+
|
127
|
+
# @return [String] The user-assigned label for the aggregation block.
|
128
|
+
def aggregation_block_label
|
129
|
+
@failure_aggregator.block_label
|
130
|
+
end
|
131
|
+
|
132
|
+
# @return [Hash] The metadata hash passed to `aggregate_failures`.
|
133
|
+
def aggregation_metadata
|
134
|
+
@failure_aggregator.metadata
|
135
|
+
end
|
136
|
+
|
137
|
+
# @return [String] A summary of the failure, including the block label and a count of failures.
|
138
|
+
def summary
|
139
|
+
"Got #{exception_count_description} from failure aggregation " \
|
140
|
+
"block#{block_description}"
|
141
|
+
end
|
142
|
+
|
143
|
+
# return [String] A description of the failure/error counts.
|
144
|
+
def exception_count_description
|
145
|
+
failure_count = pluralize("failure", failures.size)
|
146
|
+
return failure_count if other_errors.empty?
|
147
|
+
error_count = pluralize("other error", other_errors.size)
|
148
|
+
"#{failure_count} and #{error_count}"
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def initialize(failure_aggregator)
|
154
|
+
@failure_aggregator = failure_aggregator
|
155
|
+
@all_exceptions = failures + other_errors
|
156
|
+
end
|
157
|
+
|
158
|
+
def block_description
|
159
|
+
return "" unless aggregation_block_label
|
160
|
+
" #{aggregation_block_label.inspect}"
|
161
|
+
end
|
162
|
+
|
163
|
+
def pluralize(noun, count)
|
164
|
+
"#{count} #{noun}#{'s' unless count == 1}"
|
165
|
+
end
|
166
|
+
|
167
|
+
def enumerated(exceptions, index_offset)
|
168
|
+
exceptions.each_with_index.map do |exception, index|
|
169
|
+
index += index_offset
|
170
|
+
formatted_message = "#{yield exception}\n#{format_backtrace(exception.backtrace).first}"
|
171
|
+
"#{index_label index}#{indented formatted_message, index}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def exclusion_patterns
|
176
|
+
patterns = %w[/lib\d*/ruby/ bin/ exe/rspec /lib/bundler/ /exe/bundle:]
|
177
|
+
patterns << "org/jruby/" if RSpec::Support::Ruby.jruby?
|
178
|
+
patterns.map! { |s| Regexp.new(s.gsub('/', File::SEPARATOR)) }
|
179
|
+
end
|
180
|
+
|
181
|
+
def format_backtrace(backtrace)
|
182
|
+
backtrace.map { |l| backtrace_line(l) }.compact.tap { |filtered| filtered.concat backtrace if filtered.empty? }
|
183
|
+
end
|
184
|
+
|
185
|
+
def backtrace_line(line)
|
186
|
+
return if [Regexp.union(RSpec::CallerFilter::IGNORE_REGEX, *exclusion_patterns)].any? { |p| line =~ p }
|
187
|
+
|
188
|
+
# It changes the current path that is relative to
|
189
|
+
# system root to be relative to the project root.
|
190
|
+
line.sub(/(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/, '\\1.\\2'.freeze).sub(/\A([^:]+:\d+)$/, '\\1'.freeze)
|
191
|
+
end
|
192
|
+
|
193
|
+
def enumerated_failures
|
194
|
+
enumerated(failures, 0, &:message)
|
195
|
+
end
|
196
|
+
|
197
|
+
def enumerated_errors
|
198
|
+
enumerated(other_errors, failures.size) do |error|
|
199
|
+
"#{error.class}: #{error.message}"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def indented(failure_message, index)
|
204
|
+
line_1, *rest = failure_message.strip.lines.to_a
|
205
|
+
first_line_indentation = ' ' * (longest_index_label_width - width_of_label(index))
|
206
|
+
|
207
|
+
first_line_indentation + line_1 + rest.map do |line|
|
208
|
+
line =~ /\S/ ? indentation + line : line
|
209
|
+
end.join
|
210
|
+
end
|
211
|
+
|
212
|
+
def indentation
|
213
|
+
@indentation ||= ' ' * longest_index_label_width
|
214
|
+
end
|
215
|
+
|
216
|
+
def longest_index_label_width
|
217
|
+
@longest_index_label_width ||= width_of_label(failures.size)
|
218
|
+
end
|
219
|
+
|
220
|
+
def width_of_label(index)
|
221
|
+
index_label(index).chars.count
|
222
|
+
end
|
223
|
+
|
224
|
+
def index_label(index)
|
225
|
+
" #{index + 1}) "
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -17,14 +17,17 @@ module RSpec
|
|
17
17
|
#
|
18
18
|
# @private
|
19
19
|
def self.modern_matcher_from(matcher)
|
20
|
-
|
21
|
-
|
20
|
+
LegacyMatcherAdapter::RSpec2.wrap(matcher) ||
|
21
|
+
LegacyMatcherAdapter::RSpec1.wrap(matcher) || matcher
|
22
22
|
end
|
23
23
|
|
24
|
-
def self.
|
24
|
+
def self.with_matcher(handler, matcher, message)
|
25
25
|
check_message(message)
|
26
|
+
matcher = modern_matcher_from(matcher)
|
27
|
+
yield matcher
|
28
|
+
ensure
|
26
29
|
::RSpec::Matchers.last_expectation_handler = handler
|
27
|
-
::RSpec::Matchers.last_matcher =
|
30
|
+
::RSpec::Matchers.last_matcher = matcher
|
28
31
|
end
|
29
32
|
|
30
33
|
def self.handle_failure(matcher, message, failure_message_method)
|
@@ -41,15 +44,21 @@ module RSpec
|
|
41
44
|
|
42
45
|
# @private
|
43
46
|
class PositiveExpectationHandler
|
44
|
-
def self.handle_matcher(actual, initial_matcher,
|
45
|
-
|
47
|
+
def self.handle_matcher(actual, initial_matcher, custom_message=nil, &block)
|
48
|
+
ExpectationHelper.with_matcher(self, initial_matcher, custom_message) do |matcher|
|
49
|
+
return ::RSpec::Matchers::BuiltIn::PositiveOperatorMatcher.new(actual) unless initial_matcher
|
46
50
|
|
47
|
-
|
48
|
-
|
51
|
+
match_result = matcher.matches?(actual, &block)
|
52
|
+
if custom_message && match_result.respond_to?(:error_generator)
|
53
|
+
match_result.error_generator.opts[:message] = custom_message
|
54
|
+
end
|
55
|
+
|
56
|
+
match_result || ExpectationHelper.handle_failure(matcher, custom_message, :failure_message)
|
57
|
+
end
|
49
58
|
end
|
50
59
|
|
51
60
|
def self.verb
|
52
|
-
|
61
|
+
'is expected to'
|
53
62
|
end
|
54
63
|
|
55
64
|
def self.should_method
|
@@ -63,11 +72,17 @@ module RSpec
|
|
63
72
|
|
64
73
|
# @private
|
65
74
|
class NegativeExpectationHandler
|
66
|
-
def self.handle_matcher(actual, initial_matcher,
|
67
|
-
|
75
|
+
def self.handle_matcher(actual, initial_matcher, custom_message=nil, &block)
|
76
|
+
ExpectationHelper.with_matcher(self, initial_matcher, custom_message) do |matcher|
|
77
|
+
return ::RSpec::Matchers::BuiltIn::NegativeOperatorMatcher.new(actual) unless initial_matcher
|
78
|
+
|
79
|
+
negated_match_result = does_not_match?(matcher, actual, &block)
|
80
|
+
if custom_message && negated_match_result.respond_to?(:error_generator)
|
81
|
+
negated_match_result.error_generator.opts[:message] = custom_message
|
82
|
+
end
|
68
83
|
|
69
|
-
|
70
|
-
|
84
|
+
negated_match_result || ExpectationHelper.handle_failure(matcher, custom_message, :failure_message_when_negated)
|
85
|
+
end
|
71
86
|
end
|
72
87
|
|
73
88
|
def self.does_not_match?(matcher, actual, &block)
|
@@ -79,7 +94,7 @@ module RSpec
|
|
79
94
|
end
|
80
95
|
|
81
96
|
def self.verb
|
82
|
-
|
97
|
+
'is expected not to'
|
83
98
|
end
|
84
99
|
|
85
100
|
def self.should_method
|
@@ -95,7 +110,7 @@ module RSpec
|
|
95
110
|
# order to present the current protocol.
|
96
111
|
#
|
97
112
|
# @private
|
98
|
-
class
|
113
|
+
class LegacyMatcherAdapter < Matchers::MatcherDelegator
|
99
114
|
def initialize(matcher)
|
100
115
|
super
|
101
116
|
::RSpec.warn_deprecation(<<-EOS.gsub(/^\s+\|/, ''), :type => "legacy_matcher")
|
@@ -157,5 +172,11 @@ module RSpec
|
|
157
172
|
end
|
158
173
|
end
|
159
174
|
end
|
175
|
+
|
176
|
+
# RSpec 3.0 was released with the class name misspelled. For SemVer compatibility,
|
177
|
+
# we will provide this misspelled alias until 4.0.
|
178
|
+
# @deprecated Use LegacyMatcherAdapter instead.
|
179
|
+
# @private
|
180
|
+
LegacyMacherAdapter = LegacyMatcherAdapter
|
160
181
|
end
|
161
182
|
end
|
@@ -3,15 +3,56 @@ require 'rspec/expectations'
|
|
3
3
|
Minitest::Test.class_eval do
|
4
4
|
include ::RSpec::Matchers
|
5
5
|
|
6
|
+
# This `expect` will only be called if the user is using Minitest < 5.6
|
7
|
+
# or if they are _not_ using Minitest::Spec on 5.6+. Minitest::Spec on 5.6+
|
8
|
+
# defines its own `expect` and will have the assertions incremented via our
|
9
|
+
# definitions of `to`/`not_to`/`to_not` below.
|
6
10
|
def expect(*a, &b)
|
7
|
-
|
11
|
+
self.assertions += 1
|
8
12
|
super
|
9
13
|
end
|
14
|
+
|
15
|
+
# Convert a `MultipleExpectationsNotMetError` to a `Minitest::Assertion` error so
|
16
|
+
# it gets counted in minitest's summary stats as a failure rather than an error.
|
17
|
+
# It would be nice to make `MultipleExpectationsNotMetError` subclass
|
18
|
+
# `Minitest::Assertion`, but Minitest's implementation does not treat subclasses
|
19
|
+
# the same, so this is the best we can do.
|
20
|
+
def aggregate_failures(*args, &block)
|
21
|
+
super
|
22
|
+
rescue RSpec::Expectations::MultipleExpectationsNotMetError => e
|
23
|
+
assertion_failed = Minitest::Assertion.new(e.message)
|
24
|
+
assertion_failed.set_backtrace e.backtrace
|
25
|
+
raise assertion_failed
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Older versions of Minitest (e.g. before 5.6) do not define
|
30
|
+
# `Minitest::Expectation`.
|
31
|
+
if defined?(::Minitest::Expectation)
|
32
|
+
Minitest::Expectation.class_eval do
|
33
|
+
include RSpec::Expectations::ExpectationTarget::InstanceMethods
|
34
|
+
|
35
|
+
def to(*args)
|
36
|
+
ctx.assertions += 1
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def not_to(*args)
|
41
|
+
ctx.assertions += 1
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_not(*args)
|
46
|
+
ctx.assertions += 1
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
10
50
|
end
|
11
51
|
|
12
52
|
module RSpec
|
13
53
|
module Expectations
|
14
54
|
remove_const :ExpectationNotMetError
|
15
|
-
|
55
|
+
# Exception raised when an expectation fails.
|
56
|
+
const_set :ExpectationNotMetError, ::Minitest::Assertion
|
16
57
|
end
|
17
58
|
end
|
@@ -27,7 +27,7 @@ module RSpec
|
|
27
27
|
|
28
28
|
RSpec.deprecate(
|
29
29
|
"Using `#{method_name}` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax",
|
30
|
-
:replacement => "the new `:expect` syntax or explicitly enable `:should`"
|
30
|
+
:replacement => "the new `:expect` syntax or explicitly enable `:should` with `config.expect_with(:rspec) { |c| c.syntax = :should }`"
|
31
31
|
)
|
32
32
|
|
33
33
|
@warn_about_should = false
|
@@ -41,12 +41,12 @@ module RSpec
|
|
41
41
|
|
42
42
|
syntax_host.module_exec do
|
43
43
|
def should(matcher=nil, message=nil, &block)
|
44
|
-
::RSpec::Expectations::Syntax.warn_about_should_unless_configured(__method__)
|
44
|
+
::RSpec::Expectations::Syntax.warn_about_should_unless_configured(::Kernel.__method__)
|
45
45
|
::RSpec::Expectations::PositiveExpectationHandler.handle_matcher(self, matcher, message, &block)
|
46
46
|
end
|
47
47
|
|
48
48
|
def should_not(matcher=nil, message=nil, &block)
|
49
|
-
::RSpec::Expectations::Syntax.warn_about_should_unless_configured(__method__)
|
49
|
+
::RSpec::Expectations::Syntax.warn_about_should_unless_configured(::Kernel.__method__)
|
50
50
|
::RSpec::Expectations::NegativeExpectationHandler.handle_matcher(self, matcher, message, &block)
|
51
51
|
end
|
52
52
|
end
|
@@ -106,7 +106,7 @@ if defined?(BasicObject)
|
|
106
106
|
# that this syntax does not always play nice with delegate/proxy objects.
|
107
107
|
# We recommend you use the non-monkeypatching `:expect` syntax instead.
|
108
108
|
class BasicObject
|
109
|
-
# @method should
|
109
|
+
# @method should(matcher, message)
|
110
110
|
# Passes if `matcher` returns true. Available on every `Object`.
|
111
111
|
# @example
|
112
112
|
# actual.should eq expected
|
@@ -118,7 +118,7 @@ if defined?(BasicObject)
|
|
118
118
|
# @note This is only available when you have enabled the `:should` syntax.
|
119
119
|
# @see RSpec::Matchers
|
120
120
|
|
121
|
-
# @method should_not
|
121
|
+
# @method should_not(matcher, message)
|
122
122
|
# Passes if `matcher` returns false. Available on every `Object`.
|
123
123
|
# @example
|
124
124
|
# actual.should_not eq expected
|
data/lib/rspec/expectations.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rspec/support'
|
2
2
|
RSpec::Support.require_rspec_support "caller_filter"
|
3
3
|
RSpec::Support.require_rspec_support "warnings"
|
4
|
+
RSpec::Support.require_rspec_support "object_formatter"
|
4
5
|
|
5
6
|
require 'rspec/matchers'
|
6
7
|
|
@@ -63,6 +64,19 @@ module RSpec
|
|
63
64
|
# the user sets an expectation, it can't be caught in their
|
64
65
|
# code by a bare `rescue`.
|
65
66
|
# @api public
|
66
|
-
ExpectationNotMetError
|
67
|
+
class ExpectationNotMetError < Exception
|
68
|
+
end
|
69
|
+
|
70
|
+
# Exception raised from `aggregate_failures` when multiple expectations fail.
|
71
|
+
#
|
72
|
+
# @note The constant is defined here but the extensive logic of this class
|
73
|
+
# is lazily defined when `FailureAggregator` is autoloaded, since we do
|
74
|
+
# not need to waste time defining that functionality unless
|
75
|
+
# `aggregate_failures` is used.
|
76
|
+
class MultipleExpectationsNotMetError < ExpectationNotMetError
|
77
|
+
end
|
78
|
+
|
79
|
+
autoload :BlockSnippetExtractor, "rspec/expectations/block_snippet_extractor"
|
80
|
+
autoload :FailureAggregator, "rspec/expectations/failure_aggregator"
|
67
81
|
end
|
68
82
|
end
|