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
@@ -4,13 +4,11 @@ module RSpec
|
|
4
4
|
# @api private
|
5
5
|
# Provides the implementation for `change`.
|
6
6
|
# Not intended to be instantiated directly.
|
7
|
-
class Change
|
8
|
-
include Composable
|
9
|
-
|
7
|
+
class Change < BaseMatcher
|
10
8
|
# @api public
|
11
9
|
# Specifies the delta of the expected change.
|
12
10
|
def by(expected_delta)
|
13
|
-
ChangeRelatively.new(
|
11
|
+
ChangeRelatively.new(change_details, expected_delta, :by) do |actual_delta|
|
14
12
|
values_match?(expected_delta, actual_delta)
|
15
13
|
end
|
16
14
|
end
|
@@ -18,7 +16,7 @@ module RSpec
|
|
18
16
|
# @api public
|
19
17
|
# Specifies a minimum delta of the expected change.
|
20
18
|
def by_at_least(minimum)
|
21
|
-
ChangeRelatively.new(
|
19
|
+
ChangeRelatively.new(change_details, minimum, :by_at_least) do |actual_delta|
|
22
20
|
actual_delta >= minimum
|
23
21
|
end
|
24
22
|
end
|
@@ -26,7 +24,7 @@ module RSpec
|
|
26
24
|
# @api public
|
27
25
|
# Specifies a maximum delta of the expected change.
|
28
26
|
def by_at_most(maximum)
|
29
|
-
ChangeRelatively.new(
|
27
|
+
ChangeRelatively.new(change_details, maximum, :by_at_most) do |actual_delta|
|
30
28
|
actual_delta <= maximum
|
31
29
|
end
|
32
30
|
end
|
@@ -34,45 +32,44 @@ module RSpec
|
|
34
32
|
# @api public
|
35
33
|
# Specifies the new value you expect.
|
36
34
|
def to(value)
|
37
|
-
ChangeToValue.new(
|
35
|
+
ChangeToValue.new(change_details, value)
|
38
36
|
end
|
39
37
|
|
40
38
|
# @api public
|
41
39
|
# Specifies the original value.
|
42
40
|
def from(value)
|
43
|
-
ChangeFromValue.new(
|
41
|
+
ChangeFromValue.new(change_details, value)
|
44
42
|
end
|
45
43
|
|
46
44
|
# @private
|
47
45
|
def matches?(event_proc)
|
48
|
-
@event_proc = event_proc
|
49
|
-
return false unless Proc === event_proc
|
50
46
|
raise_block_syntax_error if block_given?
|
51
|
-
|
52
|
-
@change_details.changed?
|
47
|
+
perform_change(event_proc) && change_details.changed?
|
53
48
|
end
|
54
49
|
|
55
50
|
def does_not_match?(event_proc)
|
56
51
|
raise_block_syntax_error if block_given?
|
57
|
-
|
52
|
+
perform_change(event_proc) && !change_details.changed?
|
58
53
|
end
|
59
54
|
|
60
55
|
# @api private
|
61
56
|
# @return [String]
|
62
57
|
def failure_message
|
63
|
-
"expected #{
|
58
|
+
"expected #{change_details.value_representation} to have changed, " \
|
59
|
+
"but #{positive_failure_reason}"
|
64
60
|
end
|
65
61
|
|
66
62
|
# @api private
|
67
63
|
# @return [String]
|
68
64
|
def failure_message_when_negated
|
69
|
-
"expected #{
|
65
|
+
"expected #{change_details.value_representation} not to have changed, " \
|
66
|
+
"but #{negative_failure_reason}"
|
70
67
|
end
|
71
68
|
|
72
69
|
# @api private
|
73
70
|
# @return [String]
|
74
71
|
def description
|
75
|
-
"change #{
|
72
|
+
"change #{change_details.value_representation}"
|
76
73
|
end
|
77
74
|
|
78
75
|
# @private
|
@@ -80,33 +77,55 @@ module RSpec
|
|
80
77
|
true
|
81
78
|
end
|
82
79
|
|
80
|
+
# @private
|
81
|
+
def supports_value_expectations?
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
83
85
|
private
|
84
86
|
|
85
87
|
def initialize(receiver=nil, message=nil, &block)
|
86
|
-
@
|
88
|
+
@receiver = receiver
|
89
|
+
@message = message
|
90
|
+
@block = block
|
91
|
+
end
|
92
|
+
|
93
|
+
def change_details
|
94
|
+
@change_details ||= ChangeDetails.new(matcher_name, @receiver, @message, &@block)
|
95
|
+
end
|
96
|
+
|
97
|
+
def perform_change(event_proc)
|
98
|
+
@event_proc = event_proc
|
99
|
+
change_details.perform_change(event_proc) do |actual_before|
|
100
|
+
# pre-compute values derived from the `before` value before the
|
101
|
+
# mutation is applied, in case the specified mutation is mutation
|
102
|
+
# of a single object (rather than a changing what object a method
|
103
|
+
# returns). We need to cache these values before the `before` value
|
104
|
+
# they are based on potentially gets mutated.
|
105
|
+
@actual_before_description = description_of(actual_before)
|
106
|
+
end
|
87
107
|
end
|
88
108
|
|
89
109
|
def raise_block_syntax_error
|
90
|
-
raise SyntaxError, "
|
91
|
-
|
110
|
+
raise SyntaxError, "Block not received by the `change` matcher. " \
|
111
|
+
"Perhaps you want to use `{ ... }` instead of do/end?"
|
92
112
|
end
|
93
113
|
|
94
114
|
def positive_failure_reason
|
95
115
|
return "was not given a block" unless Proc === @event_proc
|
96
|
-
"is still #{
|
116
|
+
"is still #{@actual_before_description}"
|
97
117
|
end
|
98
118
|
|
99
119
|
def negative_failure_reason
|
100
120
|
return "was not given a block" unless Proc === @event_proc
|
101
|
-
"did change from #{
|
121
|
+
"did change from #{@actual_before_description} " \
|
122
|
+
"to #{description_of change_details.actual_after}"
|
102
123
|
end
|
103
124
|
end
|
104
125
|
|
105
126
|
# Used to specify a relative change.
|
106
127
|
# @api private
|
107
|
-
class ChangeRelatively
|
108
|
-
include Composable
|
109
|
-
|
128
|
+
class ChangeRelatively < BaseMatcher
|
110
129
|
def initialize(change_details, expected_delta, relativity, &comparer)
|
111
130
|
@change_details = change_details
|
112
131
|
@expected_delta = expected_delta
|
@@ -116,25 +135,27 @@ module RSpec
|
|
116
135
|
|
117
136
|
# @private
|
118
137
|
def failure_message
|
119
|
-
"expected #{@change_details.
|
138
|
+
"expected #{@change_details.value_representation} to have changed " \
|
139
|
+
"#{@relativity.to_s.tr('_', ' ')} " \
|
140
|
+
"#{description_of @expected_delta}, but #{failure_reason}"
|
120
141
|
end
|
121
142
|
|
122
143
|
# @private
|
123
144
|
def matches?(event_proc)
|
124
145
|
@event_proc = event_proc
|
125
|
-
|
126
|
-
@change_details.perform_change(event_proc)
|
127
|
-
@comparer.call(@change_details.actual_delta)
|
146
|
+
@change_details.perform_change(event_proc) && @comparer.call(@change_details.actual_delta)
|
128
147
|
end
|
129
148
|
|
130
149
|
# @private
|
131
150
|
def does_not_match?(_event_proc)
|
132
|
-
raise NotImplementedError, "`expect { }.not_to change
|
151
|
+
raise NotImplementedError, "`expect { }.not_to change " \
|
152
|
+
"{ }.#{@relativity}()` is not supported"
|
133
153
|
end
|
134
154
|
|
135
155
|
# @private
|
136
156
|
def description
|
137
|
-
"change #{@change_details.
|
157
|
+
"change #{@change_details.value_representation} " \
|
158
|
+
"#{@relativity.to_s.tr('_', ' ')} #{description_of @expected_delta}"
|
138
159
|
end
|
139
160
|
|
140
161
|
# @private
|
@@ -142,6 +163,11 @@ module RSpec
|
|
142
163
|
true
|
143
164
|
end
|
144
165
|
|
166
|
+
# @private
|
167
|
+
def supports_value_expectations?
|
168
|
+
false
|
169
|
+
end
|
170
|
+
|
145
171
|
private
|
146
172
|
|
147
173
|
def failure_reason
|
@@ -152,8 +178,7 @@ module RSpec
|
|
152
178
|
|
153
179
|
# @api private
|
154
180
|
# Base class for specifying a change from and/or to specific values.
|
155
|
-
class SpecificValuesChange
|
156
|
-
include Composable
|
181
|
+
class SpecificValuesChange < BaseMatcher
|
157
182
|
# @private
|
158
183
|
MATCH_ANYTHING = ::Object.ancestors.last
|
159
184
|
|
@@ -165,21 +190,18 @@ module RSpec
|
|
165
190
|
|
166
191
|
# @private
|
167
192
|
def matches?(event_proc)
|
168
|
-
|
169
|
-
return false unless Proc === event_proc
|
170
|
-
@change_details.perform_change(event_proc)
|
171
|
-
@change_details.changed? && matches_before? && matches_after?
|
193
|
+
perform_change(event_proc) && @change_details.changed? && @matches_before && matches_after?
|
172
194
|
end
|
173
195
|
|
174
196
|
# @private
|
175
197
|
def description
|
176
|
-
"change #{@change_details.
|
198
|
+
"change #{@change_details.value_representation} #{change_description}"
|
177
199
|
end
|
178
200
|
|
179
201
|
# @private
|
180
202
|
def failure_message
|
181
203
|
return not_given_a_block_failure unless Proc === @event_proc
|
182
|
-
return before_value_failure unless matches_before
|
204
|
+
return before_value_failure unless @matches_before
|
183
205
|
return did_not_change_failure unless @change_details.changed?
|
184
206
|
after_value_failure
|
185
207
|
end
|
@@ -189,10 +211,24 @@ module RSpec
|
|
189
211
|
true
|
190
212
|
end
|
191
213
|
|
214
|
+
# @private
|
215
|
+
def supports_value_expectations?
|
216
|
+
false
|
217
|
+
end
|
218
|
+
|
192
219
|
private
|
193
220
|
|
194
|
-
def
|
195
|
-
|
221
|
+
def perform_change(event_proc)
|
222
|
+
@event_proc = event_proc
|
223
|
+
@change_details.perform_change(event_proc) do |actual_before|
|
224
|
+
# pre-compute values derived from the `before` value before the
|
225
|
+
# mutation is applied, in case the specified mutation is mutation
|
226
|
+
# of a single object (rather than a changing what object a method
|
227
|
+
# returns). We need to cache these values before the `before` value
|
228
|
+
# they are based on potentially gets mutated.
|
229
|
+
@matches_before = values_match?(@expected_before, actual_before)
|
230
|
+
@actual_before_description = description_of(actual_before)
|
231
|
+
end
|
196
232
|
end
|
197
233
|
|
198
234
|
def matches_after?
|
@@ -200,23 +236,31 @@ module RSpec
|
|
200
236
|
end
|
201
237
|
|
202
238
|
def before_value_failure
|
203
|
-
"expected #{@change_details.
|
239
|
+
"expected #{@change_details.value_representation} " \
|
240
|
+
"to have initially been #{description_of @expected_before}, " \
|
241
|
+
"but was #{@actual_before_description}"
|
204
242
|
end
|
205
243
|
|
206
244
|
def after_value_failure
|
207
|
-
"expected #{@change_details.
|
245
|
+
"expected #{@change_details.value_representation} " \
|
246
|
+
"to have changed to #{description_of @expected_after}, " \
|
247
|
+
"but is now #{description_of @change_details.actual_after}"
|
208
248
|
end
|
209
249
|
|
210
250
|
def did_not_change_failure
|
211
|
-
"expected #{@change_details.
|
251
|
+
"expected #{@change_details.value_representation} " \
|
252
|
+
"to have changed #{change_description}, but did not change"
|
212
253
|
end
|
213
254
|
|
214
255
|
def did_change_failure
|
215
|
-
"expected #{@change_details.
|
256
|
+
"expected #{@change_details.value_representation} not to have changed, but " \
|
257
|
+
"did change from #{@actual_before_description} " \
|
258
|
+
"to #{description_of @change_details.actual_after}"
|
216
259
|
end
|
217
260
|
|
218
261
|
def not_given_a_block_failure
|
219
|
-
"expected #{@change_details.
|
262
|
+
"expected #{@change_details.value_representation} to have changed " \
|
263
|
+
"#{change_description}, but was not given a block"
|
220
264
|
end
|
221
265
|
end
|
222
266
|
|
@@ -240,19 +284,17 @@ module RSpec
|
|
240
284
|
# @private
|
241
285
|
def does_not_match?(event_proc)
|
242
286
|
if @description_suffix
|
243
|
-
raise NotImplementedError, "`expect { }.not_to change { }.to()`
|
287
|
+
raise NotImplementedError, "`expect { }.not_to change { }.to()` " \
|
288
|
+
"is not supported"
|
244
289
|
end
|
245
290
|
|
246
|
-
|
247
|
-
return false unless Proc === event_proc
|
248
|
-
@change_details.perform_change(event_proc)
|
249
|
-
!@change_details.changed? && matches_before?
|
291
|
+
perform_change(event_proc) && !@change_details.changed? && @matches_before
|
250
292
|
end
|
251
293
|
|
252
294
|
# @private
|
253
295
|
def failure_message_when_negated
|
254
296
|
return not_given_a_block_failure unless Proc === @event_proc
|
255
|
-
return before_value_failure unless matches_before
|
297
|
+
return before_value_failure unless @matches_before
|
256
298
|
did_change_failure
|
257
299
|
end
|
258
300
|
|
@@ -282,7 +324,8 @@ module RSpec
|
|
282
324
|
|
283
325
|
# @private
|
284
326
|
def does_not_match?(_event_proc)
|
285
|
-
raise NotImplementedError, "`expect { }.not_to change { }.to()`
|
327
|
+
raise NotImplementedError, "`expect { }.not_to change { }.to()` " \
|
328
|
+
"is not supported"
|
286
329
|
end
|
287
330
|
|
288
331
|
private
|
@@ -294,10 +337,24 @@ module RSpec
|
|
294
337
|
|
295
338
|
# @private
|
296
339
|
# Encapsulates the details of the before/after values.
|
340
|
+
#
|
341
|
+
# Note that this class exposes the `actual_after` value, to allow the
|
342
|
+
# matchers above to derive failure messages, etc from the value on demand
|
343
|
+
# as needed, but it intentionally does _not_ expose the `actual_before`
|
344
|
+
# value. Some usages of the `change` matcher mutate a specific object
|
345
|
+
# returned by the value proc, which means that failure message snippets,
|
346
|
+
# etc, which are derived from the `before` value may not be accurate if
|
347
|
+
# they are lazily computed as needed. We must pre-compute them before
|
348
|
+
# applying the change in the `expect` block. To ensure that all `change`
|
349
|
+
# matchers do that properly, we do not expose the `actual_before` value.
|
350
|
+
# Instead, matchers must pass a block to `perform_change`, which yields
|
351
|
+
# the `actual_before` value before applying the change.
|
297
352
|
class ChangeDetails
|
298
|
-
attr_reader :
|
353
|
+
attr_reader :actual_after
|
299
354
|
|
300
|
-
|
355
|
+
UNDEFINED = Module.new.freeze
|
356
|
+
|
357
|
+
def initialize(matcher_name, receiver=nil, message=nil, &block)
|
301
358
|
if receiver && !message
|
302
359
|
raise(
|
303
360
|
ArgumentError,
|
@@ -306,18 +363,56 @@ module RSpec
|
|
306
363
|
"You passed an object but no message."
|
307
364
|
)
|
308
365
|
end
|
309
|
-
|
310
|
-
@
|
366
|
+
|
367
|
+
@matcher_name = matcher_name
|
368
|
+
@receiver = receiver
|
369
|
+
@message = message
|
370
|
+
@value_proc = block
|
371
|
+
# TODO: temporary measure to mute warning of access to an initialized
|
372
|
+
# instance variable when a deprecated implicit block expectation
|
373
|
+
# syntax is used. This may be removed once `fail` is used, and the
|
374
|
+
# matcher never issues this warning.
|
375
|
+
@actual_after = UNDEFINED
|
376
|
+
end
|
377
|
+
|
378
|
+
def value_representation
|
379
|
+
@value_representation ||=
|
380
|
+
if @message
|
381
|
+
"`#{message_notation(@receiver, @message)}`"
|
382
|
+
elsif (value_block_snippet = extract_value_block_snippet)
|
383
|
+
"`#{value_block_snippet}`"
|
384
|
+
else
|
385
|
+
'result'
|
386
|
+
end
|
311
387
|
end
|
312
388
|
|
313
389
|
def perform_change(event_proc)
|
314
390
|
@actual_before = evaluate_value_proc
|
391
|
+
@before_hash = @actual_before.hash
|
392
|
+
yield @actual_before if block_given?
|
393
|
+
|
394
|
+
return false unless Proc === event_proc
|
315
395
|
event_proc.call
|
396
|
+
|
316
397
|
@actual_after = evaluate_value_proc
|
398
|
+
@actual_hash = @actual_after.hash
|
399
|
+
true
|
317
400
|
end
|
318
401
|
|
319
402
|
def changed?
|
320
|
-
|
403
|
+
# Consider it changed if either:
|
404
|
+
#
|
405
|
+
# - The before/after values are unequal
|
406
|
+
# - The before/after values have different hash values
|
407
|
+
#
|
408
|
+
# The latter case specifically handles the case when the value proc
|
409
|
+
# returns the exact same object, but it has been mutated.
|
410
|
+
#
|
411
|
+
# Note that it is not sufficient to only check the hashes; it is
|
412
|
+
# possible for two values to be unequal (and of different classes)
|
413
|
+
# but to return the same hash value. Also, some objects may change
|
414
|
+
# their hash after being compared with `==`/`!=`.
|
415
|
+
@actual_before != @actual_after || @before_hash != @actual_hash
|
321
416
|
end
|
322
417
|
|
323
418
|
def actual_delta
|
@@ -327,13 +422,26 @@ module RSpec
|
|
327
422
|
private
|
328
423
|
|
329
424
|
def evaluate_value_proc
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
425
|
+
@value_proc ? @value_proc.call : @receiver.__send__(@message)
|
426
|
+
end
|
427
|
+
|
428
|
+
def message_notation(receiver, message)
|
429
|
+
case receiver
|
430
|
+
when Module
|
431
|
+
"#{receiver}.#{message}"
|
335
432
|
else
|
336
|
-
|
433
|
+
"#{Support.class_of(receiver)}##{message}"
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
if RSpec::Support::RubyFeatures.ripper_supported?
|
438
|
+
def extract_value_block_snippet
|
439
|
+
return nil unless @value_proc
|
440
|
+
Expectations::BlockSnippetExtractor.try_extracting_single_line_body_of(@value_proc, @matcher_name)
|
441
|
+
end
|
442
|
+
else
|
443
|
+
def extract_value_block_snippet
|
444
|
+
nil
|
337
445
|
end
|
338
446
|
end
|
339
447
|
end
|