rspec-mocks 2.13.1 → 2.14.0.rc1

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.
Files changed (99) hide show
  1. data/Changelog.md +45 -4
  2. data/README.md +1 -1
  3. data/features/argument_matchers/README.md +2 -2
  4. data/features/argument_matchers/explicit.feature +2 -3
  5. data/features/argument_matchers/general_matchers.feature +2 -2
  6. data/features/argument_matchers/type_matchers.feature +3 -4
  7. data/features/message_expectations/README.md +2 -2
  8. data/features/message_expectations/any_instance.feature +2 -2
  9. data/features/message_expectations/block_local_expectations.feature.pending +3 -3
  10. data/features/message_expectations/expect_message_using_expect.feature +103 -0
  11. data/features/message_expectations/expect_message_using_should_receive.feature +118 -0
  12. data/features/message_expectations/receive_counts.feature +1 -1
  13. data/features/method_stubs/README.md +1 -1
  14. data/features/method_stubs/any_instance.feature +11 -11
  15. data/features/method_stubs/as_null_object.feature +1 -1
  16. data/features/method_stubs/stub_implementation.feature +2 -2
  17. data/features/outside_rspec/configuration.feature +0 -20
  18. data/features/spies/spy_partial_mock_method.feature +34 -0
  19. data/features/spies/spy_pure_mock_method.feature +76 -0
  20. data/features/spies/spy_unstubbed_method.feature +18 -0
  21. data/features/step_definitions/additional_cli_steps.rb +7 -0
  22. data/features/test_frameworks/test_unit.feature +43 -0
  23. data/lib/rspec/mocks.rb +9 -34
  24. data/lib/rspec/mocks/any_instance/chain.rb +8 -2
  25. data/lib/rspec/mocks/any_instance/expectation_chain.rb +19 -16
  26. data/lib/rspec/mocks/any_instance/recorder.rb +6 -3
  27. data/lib/rspec/mocks/any_instance/stub_chain.rb +11 -11
  28. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +8 -10
  29. data/lib/rspec/mocks/argument_list_matcher.rb +7 -3
  30. data/lib/rspec/mocks/configuration.rb +28 -1
  31. data/lib/rspec/mocks/deprecation.rb +18 -0
  32. data/lib/rspec/mocks/error_generator.rb +60 -8
  33. data/lib/rspec/mocks/errors.rb +1 -1
  34. data/lib/rspec/mocks/example_methods.rb +39 -3
  35. data/lib/rspec/mocks/extensions/marshal.rb +4 -10
  36. data/lib/rspec/mocks/framework.rb +16 -4
  37. data/lib/rspec/mocks/instance_method_stasher.rb +3 -0
  38. data/lib/rspec/mocks/matchers/have_received.rb +93 -0
  39. data/lib/rspec/mocks/matchers/receive.rb +92 -0
  40. data/lib/rspec/mocks/message_expectation.rb +66 -129
  41. data/lib/rspec/mocks/method_double.rb +50 -43
  42. data/lib/rspec/mocks/mutate_const.rb +8 -20
  43. data/lib/rspec/mocks/proxy.rb +41 -25
  44. data/lib/rspec/mocks/proxy_for_nil.rb +36 -0
  45. data/lib/rspec/mocks/space.rb +64 -11
  46. data/lib/rspec/mocks/stub_chain.rb +51 -0
  47. data/lib/rspec/mocks/syntax.rb +329 -0
  48. data/lib/rspec/mocks/targets.rb +69 -0
  49. data/lib/rspec/mocks/test_double.rb +25 -4
  50. data/lib/rspec/mocks/version.rb +1 -1
  51. data/lib/spec/mocks.rb +1 -3
  52. data/spec/rspec/mocks/and_call_original_spec.rb +8 -0
  53. data/spec/rspec/mocks/and_yield_spec.rb +6 -6
  54. data/spec/rspec/mocks/any_instance_spec.rb +43 -31
  55. data/spec/rspec/mocks/any_number_of_times_spec.rb +6 -0
  56. data/spec/rspec/mocks/argument_expectation_spec.rb +12 -14
  57. data/spec/rspec/mocks/at_least_spec.rb +46 -37
  58. data/spec/rspec/mocks/at_most_spec.rb +12 -12
  59. data/spec/rspec/mocks/block_return_value_spec.rb +18 -1
  60. data/spec/rspec/mocks/bug_report_10260_spec.rb +1 -1
  61. data/spec/rspec/mocks/bug_report_10263_spec.rb +1 -1
  62. data/spec/rspec/mocks/bug_report_11545_spec.rb +4 -4
  63. data/spec/rspec/mocks/bug_report_600_spec.rb +1 -1
  64. data/spec/rspec/mocks/bug_report_7611_spec.rb +1 -1
  65. data/spec/rspec/mocks/configuration_spec.rb +124 -0
  66. data/spec/rspec/mocks/double_spec.rb +13 -1
  67. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +17 -1
  68. data/spec/rspec/mocks/hash_excluding_matcher_spec.rb +13 -13
  69. data/spec/rspec/mocks/matchers/have_received_spec.rb +266 -0
  70. data/spec/rspec/mocks/matchers/receive_spec.rb +318 -0
  71. data/spec/rspec/mocks/methods_spec.rb +27 -0
  72. data/spec/rspec/mocks/mock_ordering_spec.rb +4 -4
  73. data/spec/rspec/mocks/mock_space_spec.rb +94 -39
  74. data/spec/rspec/mocks/mock_spec.rb +65 -50
  75. data/spec/rspec/mocks/multiple_return_value_spec.rb +10 -10
  76. data/spec/rspec/mocks/mutate_const_spec.rb +21 -1
  77. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +10 -4
  78. data/spec/rspec/mocks/null_object_mock_spec.rb +11 -2
  79. data/spec/rspec/mocks/once_counts_spec.rb +5 -5
  80. data/spec/rspec/mocks/options_hash_spec.rb +4 -4
  81. data/spec/rspec/mocks/partial_mock_spec.rb +20 -11
  82. data/spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb +7 -7
  83. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +2 -2
  84. data/spec/rspec/mocks/precise_counts_spec.rb +6 -6
  85. data/spec/rspec/mocks/serialization_spec.rb +1 -22
  86. data/spec/rspec/mocks/stash_spec.rb +4 -12
  87. data/spec/rspec/mocks/stub_implementation_spec.rb +3 -3
  88. data/spec/rspec/mocks/stub_spec.rb +44 -20
  89. data/spec/rspec/mocks/stubbed_message_expectations_spec.rb +6 -6
  90. data/spec/rspec/mocks/twice_counts_spec.rb +6 -6
  91. data/spec/rspec/mocks_spec.rb +1 -3
  92. data/spec/spec_helper.rb +25 -1
  93. metadata +86 -81
  94. data/features/message_expectations/expect_message.feature +0 -94
  95. data/lib/rspec/mocks/any_instance.rb +0 -81
  96. data/lib/rspec/mocks/extensions/psych.rb +0 -23
  97. data/lib/rspec/mocks/methods.rb +0 -155
  98. data/lib/rspec/mocks/serialization.rb +0 -34
  99. data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +0 -197
@@ -0,0 +1,92 @@
1
+ module RSpec
2
+ module Mocks
3
+ module Matchers
4
+ class Receive
5
+ def initialize(message, block)
6
+ @message = message
7
+ @block = block
8
+ @recorded_customizations = []
9
+
10
+ # MRI, JRuby and RBX report the caller inconsistently; MRI
11
+ # reports an extra "in `new'" line in the backtrace that the
12
+ # others do not include. The safest way to find the right
13
+ # line is to search for the first line BEFORE rspec/mocks/syntax.rb.
14
+ @backtrace_line = caller.find do |line|
15
+ !line.split(':').first.end_with?('rspec/mocks/syntax.rb')
16
+ end
17
+ end
18
+
19
+ def setup_expectation(subject, &block)
20
+ setup_mock_proxy_method_substitute(subject, :add_message_expectation, block)
21
+ end
22
+ alias matches? setup_expectation
23
+
24
+ def setup_negative_expectation(subject, &block)
25
+ setup_mock_proxy_method_substitute(subject, :add_negative_message_expectation, block)
26
+ end
27
+ alias does_not_match? setup_negative_expectation
28
+
29
+ def setup_allowance(subject, &block)
30
+ setup_mock_proxy_method_substitute(subject, :add_stub, block)
31
+ end
32
+
33
+ def setup_any_instance_expectation(subject, &block)
34
+ setup_any_instance_method_substitute(subject, :should_receive, block)
35
+ end
36
+
37
+ def setup_any_instance_negative_expectation(subject, &block)
38
+ setup_any_instance_method_substitute(subject, :should_not_receive, block)
39
+ end
40
+
41
+ def setup_any_instance_allowance(subject, &block)
42
+ setup_any_instance_method_substitute(subject, :stub, block)
43
+ end
44
+
45
+ MessageExpectation.public_instance_methods(false).each do |method|
46
+ next if method_defined?(method)
47
+
48
+ class_eval(<<-RUBY)
49
+ def #{method}(*args, &block)
50
+ @recorded_customizations << Customization.new(#{method.inspect}, args, block)
51
+ self
52
+ end
53
+ RUBY
54
+ end
55
+
56
+ private
57
+
58
+ def setup_mock_proxy_method_substitute(subject, method, block)
59
+ proxy = ::RSpec::Mocks.proxy_for(subject)
60
+ setup_method_substitute(proxy, method, block, @backtrace_line)
61
+ end
62
+
63
+ def setup_any_instance_method_substitute(subject, method, block)
64
+ any_instance_recorder = ::RSpec::Mocks.any_instance_recorder_for(subject)
65
+ setup_method_substitute(any_instance_recorder, method, block)
66
+ end
67
+
68
+ def setup_method_substitute(host, method, block, *args)
69
+ args << @message.to_sym
70
+ expectation = host.__send__(method, *args, &(@block || block))
71
+
72
+ @recorded_customizations.each do |customization|
73
+ customization.playback_onto(expectation)
74
+ end
75
+ end
76
+
77
+ class Customization
78
+ def initialize(method_name, args, block)
79
+ @method_name = method_name
80
+ @args = args
81
+ @block = block
82
+ end
83
+
84
+ def playback_onto(expectation)
85
+ expectation.__send__(@method_name, *@args, &@block)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+
@@ -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={}, &implementation_block)
13
+ expected_received_count=1, opts={}, &implementation)
14
14
  @error_generator = error_generator
15
15
  @error_generator.opts = opts
16
16
  @expected_from = expected_from
@@ -24,9 +24,8 @@ module RSpec
24
24
  @args_to_yield = []
25
25
  @failed_fast = nil
26
26
  @eval_context = nil
27
-
28
- @implementation = Implementation.new
29
- self.inner_implementation_action = implementation_block
27
+ @implementation = implementation
28
+ @values_to_return = nil
30
29
  end
31
30
 
32
31
  # @private
@@ -77,12 +76,11 @@ module RSpec
77
76
 
78
77
  if implementation
79
78
  # TODO: deprecate `and_return { value }`
80
- self.inner_implementation_action = implementation
79
+ @implementation = implementation
81
80
  else
82
- self.terminal_implementation_action = AndReturnImplementation.new(values)
81
+ @values_to_return = values
82
+ @implementation = build_implementation
83
83
  end
84
-
85
- nil
86
84
  end
87
85
 
88
86
  # Tells the object to delegate to the original unmodified method
@@ -100,7 +98,7 @@ module RSpec
100
98
  if @method_double.object.is_a?(RSpec::Mocks::TestDouble)
101
99
  @error_generator.raise_only_valid_on_a_partial_mock(:and_call_original)
102
100
  else
103
- @implementation = AndCallOriginalImplementation.new(@method_double.original_method)
101
+ @implementation = @method_double.original_method
104
102
  end
105
103
  end
106
104
 
@@ -130,8 +128,7 @@ module RSpec
130
128
  exception = message ? exception.exception(message) : exception.exception
131
129
  end
132
130
 
133
- self.terminal_implementation_action = Proc.new { raise exception }
134
- nil
131
+ @implementation = Proc.new { raise exception }
135
132
  end
136
133
 
137
134
  # @overload and_throw(symbol)
@@ -145,8 +142,7 @@ module RSpec
145
142
  # car.stub(:go).and_throw(:out_of_gas)
146
143
  # car.stub(:go).and_throw(:out_of_gas, :level => 0.1)
147
144
  def and_throw(*args)
148
- self.terminal_implementation_action = Proc.new { throw *args }
149
- nil
145
+ @implementation = Proc.new { throw(*args) }
150
146
  end
151
147
 
152
148
  # Tells the object to yield one or more args to a block when the message
@@ -158,7 +154,7 @@ module RSpec
158
154
  def and_yield(*args, &block)
159
155
  yield @eval_context = Object.new.extend(RSpec::Mocks::InstanceExec) if block
160
156
  @args_to_yield << args
161
- self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
157
+ @implementation = build_implementation
162
158
  self
163
159
  end
164
160
 
@@ -172,14 +168,16 @@ module RSpec
172
168
  if (@expected_received_count == 0 && !@at_least) || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
173
169
  @actual_received_count += 1
174
170
  @failed_fast = true
175
- @error_generator.raise_expectation_error(@message, @expected_received_count, @actual_received_count, *args)
171
+ #args are the args we actually received, @argument_list_matcher is the
172
+ #list of args we were expecting
173
+ @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args)
176
174
  end
177
175
 
178
176
  @order_group.handle_order_constraint self
179
177
 
180
178
  begin
181
- if implementation.present?
182
- implementation.call(*args, &block)
179
+ if @implementation
180
+ @implementation.call(*args, &block)
183
181
  elsif parent_stub
184
182
  parent_stub.invoke(nil, *args, &block)
185
183
  end
@@ -247,12 +245,23 @@ module RSpec
247
245
  # @private
248
246
  def generate_error
249
247
  if similar_messages.empty?
250
- @error_generator.raise_expectation_error(@message, @expected_received_count, @actual_received_count, *expected_args)
248
+ @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args)
251
249
  else
252
250
  @error_generator.raise_similar_message_args_error(self, *@similar_messages)
253
251
  end
254
252
  end
255
253
 
254
+ def expectation_count_type
255
+ return :at_least if @at_least
256
+ return :at_most if @at_most
257
+ return nil
258
+ end
259
+
260
+ # @private
261
+ def description
262
+ @error_generator.describe_expectation(@message, @expected_received_count, @actual_received_count, *expected_args)
263
+ end
264
+
256
265
  def raise_out_of_order_error
257
266
  @error_generator.raise_out_of_order_error @message
258
267
  end
@@ -282,7 +291,7 @@ module RSpec
282
291
  # cart.add(Book.new(:isbn => 1934356379))
283
292
  # # => passes
284
293
  def with(*args, &block)
285
- self.inner_implementation_action = block if block_given? unless args.empty?
294
+ @implementation = block if block_given? unless args.empty?
286
295
  @argument_list_matcher = ArgumentListMatcher.new(*args, &block)
287
296
  self
288
297
  end
@@ -294,7 +303,7 @@ module RSpec
294
303
  #
295
304
  # dealer.should_receive(:deal_card).exactly(10).times
296
305
  def exactly(n, &block)
297
- self.inner_implementation_action = block
306
+ @implementation = block if block
298
307
  set_expected_received_count :exactly, n
299
308
  self
300
309
  end
@@ -306,7 +315,11 @@ module RSpec
306
315
  #
307
316
  # dealer.should_receive(:deal_card).at_least(9).times
308
317
  def at_least(n, &block)
309
- self.inner_implementation_action = block
318
+ if n == 0
319
+ RSpec.deprecate "at_least(0) with should_receive", :replacement => "stub"
320
+ end
321
+
322
+ @implementation = block if block
310
323
  set_expected_received_count :at_least, n
311
324
  self
312
325
  end
@@ -318,7 +331,7 @@ module RSpec
318
331
  #
319
332
  # dealer.should_receive(:deal_card).at_most(10).times
320
333
  def at_most(n, &block)
321
- self.inner_implementation_action = block
334
+ @implementation = block if block
322
335
  set_expected_received_count :at_most, n
323
336
  self
324
337
  end
@@ -331,14 +344,15 @@ module RSpec
331
344
  # dealer.should_receive(:deal_card).at_least(10).times
332
345
  # dealer.should_receive(:deal_card).at_most(10).times
333
346
  def times(&block)
334
- self.inner_implementation_action = block
347
+ @implementation = block if block
335
348
  self
336
349
  end
337
350
 
338
351
 
339
352
  # Allows an expected message to be received any number of times.
340
353
  def any_number_of_times(&block)
341
- self.inner_implementation_action = block
354
+ RSpec.deprecate "any_number_of_times", :replacement => "stub"
355
+ @implementation = block if block
342
356
  @expected_received_count = :any
343
357
  self
344
358
  end
@@ -359,7 +373,7 @@ module RSpec
359
373
  #
360
374
  # car.should_receive(:go).once
361
375
  def once(&block)
362
- self.inner_implementation_action = block
376
+ @implementation = block if block
363
377
  set_expected_received_count :exactly, 1
364
378
  self
365
379
  end
@@ -370,7 +384,7 @@ module RSpec
370
384
  #
371
385
  # car.should_receive(:go).twice
372
386
  def twice(&block)
373
- self.inner_implementation_action = block
387
+ @implementation = block if block
374
388
  set_expected_received_count :exactly, 2
375
389
  self
376
390
  end
@@ -383,7 +397,7 @@ module RSpec
383
397
  # api.should_receive(:run).ordered
384
398
  # api.should_receive(:finish).ordered
385
399
  def ordered(&block)
386
- self.inner_implementation_action = block
400
+ @implementation = block if block
387
401
  @order_group.register(self)
388
402
  @ordered = true
389
403
  self
@@ -404,7 +418,7 @@ module RSpec
404
418
  @actual_received_count += 1
405
419
  end
406
420
 
407
- private
421
+ protected
408
422
 
409
423
  def failed_fast?
410
424
  @failed_fast
@@ -421,16 +435,13 @@ module RSpec
421
435
  end
422
436
  end
423
437
 
424
- def initial_implementation_action=(action)
425
- implementation.initial_action = action
426
- end
427
-
428
- def inner_implementation_action=(action)
429
- implementation.inner_action = action if action
430
- end
438
+ private
431
439
 
432
- def terminal_implementation_action=(action)
433
- implementation.terminal_action = action
440
+ def build_implementation
441
+ Implementation.new(
442
+ @values_to_return, @args_to_yield,
443
+ @eval_context, @error_generator
444
+ ).method(:call)
434
445
  end
435
446
  end
436
447
 
@@ -441,13 +452,10 @@ module RSpec
441
452
  super(error_generator, expectation_ordering, expected_from, method_double, 0, {}, &implementation)
442
453
  end
443
454
 
455
+ # no-op
456
+ # @deprecated and_return is not supported with negative message expectations.
444
457
  def and_return(*)
445
- # no-op
446
- # @deprecated and_return is not supported with negative message expectations.
447
- RSpec::Mocks.warn_deprecation <<-MSG
448
-
449
- DEPRECATION: `and_return` with `should_not_receive` is deprecated. Called from #{caller(0)[1]}
450
- MSG
458
+ RSpec.deprecate "and_return with should_not_receive"
451
459
  end
452
460
 
453
461
  # @private
@@ -456,20 +464,29 @@ MSG
456
464
  end
457
465
  end
458
466
 
459
- # Handles the implementation of an `and_yield` declaration.
467
+ # Represents a configured implementation. Takes into account
468
+ # `and_return` and `and_yield` instructions.
460
469
  # @private
461
- class AndYieldImplementation
462
- def initialize(args_to_yield, eval_context, error_generator)
470
+ class Implementation
471
+ def initialize(values_to_return, args_to_yield, eval_context, error_generator)
472
+ @values_to_return = values_to_return
463
473
  @args_to_yield = args_to_yield
464
474
  @eval_context = eval_context
465
475
  @error_generator = error_generator
466
476
  end
467
477
 
468
- def arity
469
- 0
478
+ def call(*args_to_ignore, &block)
479
+ default_return_value = perform_yield(&block)
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
470
487
  end
471
488
 
472
- def call(&block)
489
+ def perform_yield(&block)
473
490
  return if @args_to_yield.empty? && @eval_context.nil?
474
491
 
475
492
  @error_generator.raise_missing_block_error @args_to_yield unless block
@@ -483,85 +500,5 @@ MSG
483
500
  value
484
501
  end
485
502
  end
486
-
487
- # Handles the implementation of an `and_return` implementation.
488
- # @private
489
- class AndReturnImplementation
490
- def initialize(values_to_return)
491
- @values_to_return = values_to_return
492
- end
493
-
494
- def arity
495
- 0
496
- end
497
-
498
- def call(&block)
499
- if @values_to_return.size > 1
500
- @values_to_return.shift
501
- else
502
- @values_to_return.first
503
- end
504
- end
505
- end
506
-
507
- # Represents a configured implementation. Takes into account
508
- # any number of sub-implementations.
509
- # @private
510
- class Implementation
511
- attr_accessor :initial_action, :inner_action, :terminal_action
512
-
513
- def call(*args, &block)
514
- actions.map do |action|
515
- action.arity.zero? ? action.call(&block) : action.call(*args, &block)
516
- end.last
517
- end
518
-
519
- def present?
520
- actions.any?
521
- end
522
-
523
- private
524
-
525
- def actions
526
- [initial_action, inner_action, terminal_action].compact
527
- end
528
- end
529
-
530
- # Represents an `and_call_original` implementation.
531
- # @private
532
- class AndCallOriginalImplementation
533
- def initialize(method)
534
- @method = method
535
- end
536
-
537
- CannotModifyFurtherError = Class.new(StandardError)
538
-
539
- def initial_action=(value)
540
- raise cannot_modify_further_error
541
- end
542
-
543
- def inner_action=(value)
544
- raise cannot_modify_further_error
545
- end
546
-
547
- def terminal_action=(value)
548
- raise cannot_modify_further_error
549
- end
550
-
551
- def present?
552
- true
553
- end
554
-
555
- def call(*args, &block)
556
- @method.call(*args, &block)
557
- end
558
-
559
- private
560
-
561
- def cannot_modify_further_error
562
- CannotModifyFurtherError.new "This method has already been configured " +
563
- "to call the original implementation, and cannot be modified further."
564
- end
565
- end
566
503
  end
567
504
  end