rspec-mocks 2.14.0.rc1 → 2.14.0
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.
- data/Changelog.md +25 -0
- data/features/message_expectations/allow_any_instance_of.feature +26 -0
- data/features/message_expectations/expect_any_instance_of.feature +27 -0
- data/features/message_expectations/expect_message_using_expect.feature +4 -0
- data/features/method_stubs/README.md +28 -2
- data/features/method_stubs/any_instance.feature +5 -1
- data/features/method_stubs/as_null_object.feature +6 -1
- data/features/method_stubs/simple_return_value_with_allow.feature +44 -0
- data/features/method_stubs/{simple_return_value.feature → simple_return_value_with_stub.feature} +0 -0
- data/features/method_stubs/stub_implementation.feature +23 -1
- data/lib/rspec/mocks.rb +35 -0
- data/lib/rspec/mocks/any_instance/chain.rb +27 -18
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +1 -28
- data/lib/rspec/mocks/any_instance/recorder.rb +1 -2
- data/lib/rspec/mocks/any_instance/stub_chain.rb +2 -1
- data/lib/rspec/mocks/error_generator.rb +7 -0
- data/lib/rspec/mocks/extensions/marshal.rb +7 -7
- data/lib/rspec/mocks/matchers/receive.rb +5 -1
- data/lib/rspec/mocks/message_expectation.rb +142 -63
- data/lib/rspec/mocks/method_double.rb +0 -9
- data/lib/rspec/mocks/proxy.rb +0 -5
- data/lib/rspec/mocks/proxy_for_nil.rb +2 -1
- data/lib/rspec/mocks/syntax.rb +15 -5
- data/lib/rspec/mocks/test_double.rb +2 -2
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/any_instance_spec.rb +26 -0
- data/spec/rspec/mocks/block_return_value_spec.rb +18 -0
- data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +205 -0
- data/spec/rspec/mocks/extensions/marshal_spec.rb +54 -0
- data/spec/rspec/mocks/matchers/receive_spec.rb +10 -0
- data/spec/rspec/mocks/mock_spec.rb +31 -3
- data/spec/rspec/mocks/partial_mock_spec.rb +3 -3
- data/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +81 -0
- data/spec/rspec/mocks/test_double_spec.rb +16 -6
- metadata +24 -9
@@ -116,6 +116,13 @@ module RSpec
|
|
116
116
|
"method has been mocked instead of stubbed."
|
117
117
|
end
|
118
118
|
|
119
|
+
def self.raise_double_negation_error(wrapped_expression)
|
120
|
+
raise "Isn't life confusing enough? You've already set a " +
|
121
|
+
"negative message expectation and now you are trying to " +
|
122
|
+
"negate it again with `never`. What does an expression like " +
|
123
|
+
"`#{wrapped_expression}.not_to receive(:msg).never` even mean?"
|
124
|
+
end
|
125
|
+
|
119
126
|
private
|
120
127
|
|
121
128
|
def intro
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Marshal
|
2
2
|
class << self
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
if
|
7
|
-
|
3
|
+
# Duplicates any mock objects before serialization. Otherwise,
|
4
|
+
# serialization will fail because methods exist on the singleton class.
|
5
|
+
def dump_with_mocks(object, *rest)
|
6
|
+
if ::RSpec::Mocks.space.nil? || !::RSpec::Mocks.space.registered?(object) || NilClass === object
|
7
|
+
dump_without_mocks(object, *rest)
|
8
|
+
else
|
9
|
+
dump_without_mocks(object.dup, *rest)
|
8
10
|
end
|
9
|
-
|
10
|
-
dump_without_mocks(*args.unshift(object.dup))
|
11
11
|
end
|
12
12
|
|
13
13
|
alias_method :dump_without_mocks, :dump
|
@@ -22,7 +22,11 @@ module RSpec
|
|
22
22
|
alias matches? setup_expectation
|
23
23
|
|
24
24
|
def setup_negative_expectation(subject, &block)
|
25
|
-
|
25
|
+
# ensure `never` goes first for cases like `never.and_return(5)`,
|
26
|
+
# where `and_return` is meant to raise an error
|
27
|
+
@recorded_customizations.unshift Customization.new(:never, [], nil)
|
28
|
+
|
29
|
+
setup_expectation(subject, &block)
|
26
30
|
end
|
27
31
|
alias does_not_match? setup_negative_expectation
|
28
32
|
|
@@ -10,7 +10,7 @@ module RSpec
|
|
10
10
|
|
11
11
|
# @private
|
12
12
|
def initialize(error_generator, expectation_ordering, expected_from, method_double,
|
13
|
-
expected_received_count=1, opts={}, &
|
13
|
+
expected_received_count=1, opts={}, &implementation_block)
|
14
14
|
@error_generator = error_generator
|
15
15
|
@error_generator.opts = opts
|
16
16
|
@expected_from = expected_from
|
@@ -24,11 +24,10 @@ module RSpec
|
|
24
24
|
@args_to_yield = []
|
25
25
|
@failed_fast = nil
|
26
26
|
@eval_context = nil
|
27
|
-
@implementation = implementation
|
28
|
-
@values_to_return = nil
|
29
|
-
end
|
30
27
|
|
31
|
-
|
28
|
+
@implementation = Implementation.new
|
29
|
+
self.inner_implementation_action = implementation_block
|
30
|
+
end
|
32
31
|
|
33
32
|
# @private
|
34
33
|
def expected_args
|
@@ -72,15 +71,20 @@ module RSpec
|
|
72
71
|
# counter.stub(:count) { 1 }
|
73
72
|
# counter.count # => 1
|
74
73
|
def and_return(*values, &implementation)
|
74
|
+
if negative?
|
75
|
+
RSpec.deprecate "`and_return` on a negative message expectation"
|
76
|
+
end
|
77
|
+
|
75
78
|
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least)
|
76
79
|
|
77
80
|
if implementation
|
78
81
|
# TODO: deprecate `and_return { value }`
|
79
|
-
|
82
|
+
self.inner_implementation_action = implementation
|
80
83
|
else
|
81
|
-
|
82
|
-
@implementation = build_implementation
|
84
|
+
self.terminal_implementation_action = AndReturnImplementation.new(values)
|
83
85
|
end
|
86
|
+
|
87
|
+
nil
|
84
88
|
end
|
85
89
|
|
86
90
|
# Tells the object to delegate to the original unmodified method
|
@@ -98,7 +102,7 @@ module RSpec
|
|
98
102
|
if @method_double.object.is_a?(RSpec::Mocks::TestDouble)
|
99
103
|
@error_generator.raise_only_valid_on_a_partial_mock(:and_call_original)
|
100
104
|
else
|
101
|
-
@implementation = @method_double.original_method
|
105
|
+
@implementation = AndCallOriginalImplementation.new(@method_double.original_method)
|
102
106
|
end
|
103
107
|
end
|
104
108
|
|
@@ -128,7 +132,8 @@ module RSpec
|
|
128
132
|
exception = message ? exception.exception(message) : exception.exception
|
129
133
|
end
|
130
134
|
|
131
|
-
|
135
|
+
self.terminal_implementation_action = Proc.new { raise exception }
|
136
|
+
nil
|
132
137
|
end
|
133
138
|
|
134
139
|
# @overload and_throw(symbol)
|
@@ -142,7 +147,8 @@ module RSpec
|
|
142
147
|
# car.stub(:go).and_throw(:out_of_gas)
|
143
148
|
# car.stub(:go).and_throw(:out_of_gas, :level => 0.1)
|
144
149
|
def and_throw(*args)
|
145
|
-
|
150
|
+
self.terminal_implementation_action = Proc.new { throw(*args) }
|
151
|
+
nil
|
146
152
|
end
|
147
153
|
|
148
154
|
# Tells the object to yield one or more args to a block when the message
|
@@ -154,7 +160,7 @@ module RSpec
|
|
154
160
|
def and_yield(*args, &block)
|
155
161
|
yield @eval_context = Object.new.extend(RSpec::Mocks::InstanceExec) if block
|
156
162
|
@args_to_yield << args
|
157
|
-
|
163
|
+
self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
|
158
164
|
self
|
159
165
|
end
|
160
166
|
|
@@ -165,7 +171,7 @@ module RSpec
|
|
165
171
|
|
166
172
|
# @private
|
167
173
|
def invoke(parent_stub, *args, &block)
|
168
|
-
if
|
174
|
+
if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
|
169
175
|
@actual_received_count += 1
|
170
176
|
@failed_fast = true
|
171
177
|
#args are the args we actually received, @argument_list_matcher is the
|
@@ -176,8 +182,8 @@ module RSpec
|
|
176
182
|
@order_group.handle_order_constraint self
|
177
183
|
|
178
184
|
begin
|
179
|
-
if
|
180
|
-
|
185
|
+
if implementation.present?
|
186
|
+
implementation.call(*args, &block)
|
181
187
|
elsif parent_stub
|
182
188
|
parent_stub.invoke(nil, *args, &block)
|
183
189
|
end
|
@@ -186,6 +192,11 @@ module RSpec
|
|
186
192
|
end
|
187
193
|
end
|
188
194
|
|
195
|
+
# @private
|
196
|
+
def negative?
|
197
|
+
@expected_received_count == 0 && !@at_least
|
198
|
+
end
|
199
|
+
|
189
200
|
# @private
|
190
201
|
def called_max_times?
|
191
202
|
@expected_received_count != :any &&
|
@@ -291,7 +302,7 @@ module RSpec
|
|
291
302
|
# cart.add(Book.new(:isbn => 1934356379))
|
292
303
|
# # => passes
|
293
304
|
def with(*args, &block)
|
294
|
-
|
305
|
+
self.inner_implementation_action = block if block_given? unless args.empty?
|
295
306
|
@argument_list_matcher = ArgumentListMatcher.new(*args, &block)
|
296
307
|
self
|
297
308
|
end
|
@@ -303,7 +314,7 @@ module RSpec
|
|
303
314
|
#
|
304
315
|
# dealer.should_receive(:deal_card).exactly(10).times
|
305
316
|
def exactly(n, &block)
|
306
|
-
|
317
|
+
self.inner_implementation_action = block
|
307
318
|
set_expected_received_count :exactly, n
|
308
319
|
self
|
309
320
|
end
|
@@ -319,7 +330,7 @@ module RSpec
|
|
319
330
|
RSpec.deprecate "at_least(0) with should_receive", :replacement => "stub"
|
320
331
|
end
|
321
332
|
|
322
|
-
|
333
|
+
self.inner_implementation_action = block
|
323
334
|
set_expected_received_count :at_least, n
|
324
335
|
self
|
325
336
|
end
|
@@ -331,7 +342,7 @@ module RSpec
|
|
331
342
|
#
|
332
343
|
# dealer.should_receive(:deal_card).at_most(10).times
|
333
344
|
def at_most(n, &block)
|
334
|
-
|
345
|
+
self.inner_implementation_action = block
|
335
346
|
set_expected_received_count :at_most, n
|
336
347
|
self
|
337
348
|
end
|
@@ -344,7 +355,7 @@ module RSpec
|
|
344
355
|
# dealer.should_receive(:deal_card).at_least(10).times
|
345
356
|
# dealer.should_receive(:deal_card).at_most(10).times
|
346
357
|
def times(&block)
|
347
|
-
|
358
|
+
self.inner_implementation_action = block
|
348
359
|
self
|
349
360
|
end
|
350
361
|
|
@@ -352,7 +363,7 @@ module RSpec
|
|
352
363
|
# Allows an expected message to be received any number of times.
|
353
364
|
def any_number_of_times(&block)
|
354
365
|
RSpec.deprecate "any_number_of_times", :replacement => "stub"
|
355
|
-
|
366
|
+
self.inner_implementation_action = block
|
356
367
|
@expected_received_count = :any
|
357
368
|
self
|
358
369
|
end
|
@@ -363,6 +374,7 @@ module RSpec
|
|
363
374
|
#
|
364
375
|
# car.should_receive(:stop).never
|
365
376
|
def never
|
377
|
+
ErrorGenerator.raise_double_negation_error("expect(obj)") if negative?
|
366
378
|
@expected_received_count = 0
|
367
379
|
self
|
368
380
|
end
|
@@ -373,7 +385,7 @@ module RSpec
|
|
373
385
|
#
|
374
386
|
# car.should_receive(:go).once
|
375
387
|
def once(&block)
|
376
|
-
|
388
|
+
self.inner_implementation_action = block
|
377
389
|
set_expected_received_count :exactly, 1
|
378
390
|
self
|
379
391
|
end
|
@@ -384,7 +396,7 @@ module RSpec
|
|
384
396
|
#
|
385
397
|
# car.should_receive(:go).twice
|
386
398
|
def twice(&block)
|
387
|
-
|
399
|
+
self.inner_implementation_action = block
|
388
400
|
set_expected_received_count :exactly, 2
|
389
401
|
self
|
390
402
|
end
|
@@ -397,7 +409,7 @@ module RSpec
|
|
397
409
|
# api.should_receive(:run).ordered
|
398
410
|
# api.should_receive(:finish).ordered
|
399
411
|
def ordered(&block)
|
400
|
-
|
412
|
+
self.inner_implementation_action = block
|
401
413
|
@order_group.register(self)
|
402
414
|
@ordered = true
|
403
415
|
self
|
@@ -405,7 +417,7 @@ module RSpec
|
|
405
417
|
|
406
418
|
# @private
|
407
419
|
def negative_expectation_for?(message)
|
408
|
-
|
420
|
+
@message == message && negative?
|
409
421
|
end
|
410
422
|
|
411
423
|
# @private
|
@@ -418,7 +430,7 @@ module RSpec
|
|
418
430
|
@actual_received_count += 1
|
419
431
|
end
|
420
432
|
|
421
|
-
|
433
|
+
private
|
422
434
|
|
423
435
|
def failed_fast?
|
424
436
|
@failed_fast
|
@@ -435,58 +447,33 @@ module RSpec
|
|
435
447
|
end
|
436
448
|
end
|
437
449
|
|
438
|
-
|
439
|
-
|
440
|
-
def build_implementation
|
441
|
-
Implementation.new(
|
442
|
-
@values_to_return, @args_to_yield,
|
443
|
-
@eval_context, @error_generator
|
444
|
-
).method(:call)
|
445
|
-
end
|
446
|
-
end
|
447
|
-
|
448
|
-
# @private
|
449
|
-
class NegativeMessageExpectation < MessageExpectation
|
450
|
-
# @private
|
451
|
-
def initialize(error_generator, expectation_ordering, expected_from, method_double, &implementation)
|
452
|
-
super(error_generator, expectation_ordering, expected_from, method_double, 0, {}, &implementation)
|
450
|
+
def initial_implementation_action=(action)
|
451
|
+
implementation.initial_action = action
|
453
452
|
end
|
454
453
|
|
455
|
-
|
456
|
-
|
457
|
-
def and_return(*)
|
458
|
-
RSpec.deprecate "and_return with should_not_receive"
|
454
|
+
def inner_implementation_action=(action)
|
455
|
+
implementation.inner_action = action if action
|
459
456
|
end
|
460
457
|
|
461
|
-
|
462
|
-
|
463
|
-
return @message == message
|
458
|
+
def terminal_implementation_action=(action)
|
459
|
+
implementation.terminal_action = action
|
464
460
|
end
|
465
461
|
end
|
466
462
|
|
467
|
-
#
|
468
|
-
# `and_return` and `and_yield` instructions.
|
463
|
+
# Handles the implementation of an `and_yield` declaration.
|
469
464
|
# @private
|
470
|
-
class
|
471
|
-
def initialize(
|
472
|
-
@values_to_return = values_to_return
|
465
|
+
class AndYieldImplementation
|
466
|
+
def initialize(args_to_yield, eval_context, error_generator)
|
473
467
|
@args_to_yield = args_to_yield
|
474
468
|
@eval_context = eval_context
|
475
469
|
@error_generator = error_generator
|
476
470
|
end
|
477
471
|
|
478
|
-
def
|
479
|
-
|
480
|
-
return default_return_value unless @values_to_return
|
481
|
-
|
482
|
-
if @values_to_return.size > 1
|
483
|
-
@values_to_return.shift
|
484
|
-
else
|
485
|
-
@values_to_return.first
|
486
|
-
end
|
472
|
+
def arity
|
473
|
+
-1
|
487
474
|
end
|
488
475
|
|
489
|
-
def
|
476
|
+
def call(*args_to_ignore, &block)
|
490
477
|
return if @args_to_yield.empty? && @eval_context.nil?
|
491
478
|
|
492
479
|
@error_generator.raise_missing_block_error @args_to_yield unless block
|
@@ -500,5 +487,97 @@ module RSpec
|
|
500
487
|
value
|
501
488
|
end
|
502
489
|
end
|
490
|
+
|
491
|
+
# Handles the implementation of an `and_return` implementation.
|
492
|
+
# @private
|
493
|
+
class AndReturnImplementation
|
494
|
+
def initialize(values_to_return)
|
495
|
+
@values_to_return = values_to_return
|
496
|
+
end
|
497
|
+
|
498
|
+
def arity
|
499
|
+
-1
|
500
|
+
end
|
501
|
+
|
502
|
+
def call(*args_to_ignore, &block)
|
503
|
+
if @values_to_return.size > 1
|
504
|
+
@values_to_return.shift
|
505
|
+
else
|
506
|
+
@values_to_return.first
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
# Represents a configured implementation. Takes into account
|
512
|
+
# any number of sub-implementations.
|
513
|
+
# @private
|
514
|
+
class Implementation
|
515
|
+
attr_accessor :initial_action, :inner_action, :terminal_action
|
516
|
+
|
517
|
+
def call(*args, &block)
|
518
|
+
actions.map do |action|
|
519
|
+
action.call(*arg_slice_for(args, action.arity), &block)
|
520
|
+
end.last
|
521
|
+
end
|
522
|
+
|
523
|
+
def arg_slice_for(args, arity)
|
524
|
+
if arity >= 0
|
525
|
+
args.slice(0, arity)
|
526
|
+
else
|
527
|
+
args
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
531
|
+
def present?
|
532
|
+
actions.any?
|
533
|
+
end
|
534
|
+
|
535
|
+
private
|
536
|
+
|
537
|
+
def actions
|
538
|
+
[initial_action, inner_action, terminal_action].compact
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
# Represents an `and_call_original` implementation.
|
543
|
+
# @private
|
544
|
+
class AndCallOriginalImplementation
|
545
|
+
def initialize(method)
|
546
|
+
@method = method
|
547
|
+
end
|
548
|
+
|
549
|
+
CannotModifyFurtherError = Class.new(StandardError)
|
550
|
+
|
551
|
+
def arity
|
552
|
+
@method.arity
|
553
|
+
end
|
554
|
+
|
555
|
+
def initial_action=(value)
|
556
|
+
raise cannot_modify_further_error
|
557
|
+
end
|
558
|
+
|
559
|
+
def inner_action=(value)
|
560
|
+
raise cannot_modify_further_error
|
561
|
+
end
|
562
|
+
|
563
|
+
def terminal_action=(value)
|
564
|
+
raise cannot_modify_further_error
|
565
|
+
end
|
566
|
+
|
567
|
+
def present?
|
568
|
+
true
|
569
|
+
end
|
570
|
+
|
571
|
+
def call(*args, &block)
|
572
|
+
@method.call(*args, &block)
|
573
|
+
end
|
574
|
+
|
575
|
+
private
|
576
|
+
|
577
|
+
def cannot_modify_further_error
|
578
|
+
CannotModifyFurtherError.new "This method has already been configured " +
|
579
|
+
"to call the original implementation, and cannot be modified further."
|
580
|
+
end
|
581
|
+
end
|
503
582
|
end
|
504
583
|
end
|
@@ -226,15 +226,6 @@ module RSpec
|
|
226
226
|
expectation
|
227
227
|
end
|
228
228
|
|
229
|
-
# @private
|
230
|
-
def add_negative_expectation(error_generator, expectation_ordering, expected_from, &implementation)
|
231
|
-
configure_method
|
232
|
-
expectation = NegativeMessageExpectation.new(error_generator, expectation_ordering,
|
233
|
-
expected_from, self, &implementation)
|
234
|
-
expectations.unshift expectation
|
235
|
-
expectation
|
236
|
-
end
|
237
|
-
|
238
229
|
# @private
|
239
230
|
def build_expectation(error_generator, expectation_ordering)
|
240
231
|
expected_from = IGNORED_BACKTRACE_LINE
|