rspec-mocks 3.8.0 → 3.12.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 20312b11c0d1afe102fb10eece7ff42580873332
4
- data.tar.gz: '09febe88bdf3e5548b4aa472cc86163597a51049'
2
+ SHA256:
3
+ metadata.gz: 51d2892643ca24dce94963c553f2fdee71d55d9c3c2b2d14ba4fb2cb4ed0876a
4
+ data.tar.gz: ceb42a276b994afe0eaffe46e3a7f314393caf5ba39a5804969b955dc9f309d0
5
5
  SHA512:
6
- metadata.gz: 25e25343ed7f1ecf2a913298c0ef36146bc6934a0d3b73eaccdc4e0645f369ec7e61dd59f811cb6bff4499d94682862e1d8ad8d56ea88f94dfa63b57dded7907
7
- data.tar.gz: 94cd883965c37989c5b0542c36e955cbb6fbd4ba94b7513326aaa14679966a5bb96449b6e7bc0deda51a77e13411fc9ee33c10c9bc25f73d2bed61770463c4c8
6
+ metadata.gz: 3d19a308278d05b7703dafc5a88ca5b897badd3e60619fce025937b2ee677e36bea53f9ff2386b4180d107ce45dc695fecb68c69e46d0a4d0ec8063126d30b5f
7
+ data.tar.gz: dd881cdd051593ef697cab19d41b760933daca59768ad12430551743936d4373c575f2cc8cfe496e955e90b3cd965d14ec1f2b097c66f63e5e35e34d720696d9
checksums.yaml.gz.sig CHANGED
Binary file
data/Changelog.md CHANGED
@@ -1,3 +1,173 @@
1
+ ### Development
2
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.6...3-12-maintenance)
3
+
4
+ ### 3.12.6 / 2023-07-11
5
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.5...v3.12.6)
6
+
7
+ Bug Fixes:
8
+
9
+ * Fix an issue with `and_call_original` when using the `method_missing` fallback
10
+ with keyword arguments. (Igor Drozdov, #1552)
11
+
12
+ ### 3.12.5 / 2023-03-30
13
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.4...v3.12.5)
14
+
15
+ Bug Fixes:
16
+
17
+ * Fix compatibility issue with Rails where active_support monkey patches `with`
18
+ when using any instance. (Lachlan Sylvester, #1540)
19
+
20
+ ### 3.12.4 / 2023-03-12
21
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.3...v3.12.4)
22
+
23
+ Bug Fixes:
24
+
25
+ * Fix an issue with asserting that Array#reverse is never called. (Brad Trick, #1533)
26
+ * Fix compatibility issue with Rails where active_support monkey patches `with`.
27
+ (Jean Boussier, #1531, #1534)
28
+
29
+ ### 3.12.3 / 2023-01-17
30
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.2...v3.12.3)
31
+
32
+ Bug Fixes:
33
+
34
+ * Fix keyword delegation in `send` for verifying doubles on Ruby 3.
35
+ (Charlie Honig, #1485)
36
+
37
+ ### 3.12.2 / 2023-01-07
38
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.1...v3.12.2)
39
+
40
+ Bug Fixes:
41
+
42
+ * Fix implementation blocks for mocks using keyword arguments on Ruby 3.2.0.
43
+ (Adam Steel, #1508)
44
+ * Fix keyword argument assertions when mocking using `with` on Ruby 3.2.0.
45
+ (Slava Kardakov, Benoit Tigeot, Phil Pirozhkov, Benoit Daloze, #1514)
46
+
47
+ ### 3.12.1 / 2022-12-10
48
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.0...v3.12.1)
49
+
50
+ Bug Fixes:
51
+
52
+ * Remove empty diff marker when a diff only contains console codes. (Jon Rowe, #1506)
53
+ * Show keyword vs hash diff marker when arguments are not `==` (Jon Rowe, #1506)
54
+ * Change check to detect frozen objects to rescue errors rather than
55
+ pre-empting by checking `frozen?` due to some objects mis-behaving.
56
+ (Keegan Roth, #1401)
57
+ * Prevent unfulfilled expectations using `expect_any_instance_of` across a class
58
+ inheritance boundary from raising rather than failing. (Jon Rowe, #1496)
59
+ * Prevent a misleading error message when using `allow(...).not_to` with
60
+ unsupported matchers. (Phil Pirozhkov, #1503)
61
+
62
+ ### 3.12.0 / 2022-10-26
63
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.2...v3.12.0)
64
+
65
+ Enhancements:
66
+
67
+ * Improve diff output when diffing keyword arguments against hashes.
68
+ (Jean Boussier, #1461)
69
+
70
+ ### 3.11.2 / 2022-10-25
71
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.1...v3.11.2)
72
+
73
+ Bug Fixes:
74
+
75
+ * Use the original implementation of `Class.new` to detect overridden definitions
76
+ of `new` rather than the owner, fixing detection of "double aliased" methods
77
+ in Ruby 3 and above. (Benoit Daloze, #1470, #1476)
78
+ * Support keyword argument semantics when constraining argument expectations using
79
+ `with` on Ruby 3.0+ with `instance_double` (Andrii Malyshko, #1473)
80
+
81
+ ### 3.11.1 / 2022-03-31
82
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.0...v3.11.1)
83
+
84
+ Bug Fixes:
85
+
86
+ * Add extra `ruby2_keywords` calls to properly designate methods using
87
+ `*args` to pass keyword around, fixes an issue with TruffleRuby.
88
+ (Benoit Daloze, #1464)
89
+
90
+ ### 3.11.0 / 2022-02-09
91
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.3...v3.11.0)
92
+
93
+ Enhancements:
94
+
95
+ * Add `and_invoke` implementation for configuring responses to `receive`
96
+ (and `receive_messages`) with multiple callable objects. (Kyle Smith, #1411)
97
+
98
+ ### 3.10.3 / 2022-01-28
99
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.2...v3.10.3)
100
+
101
+ Bug Fixes:
102
+
103
+ * Suppress warning by setting `$VERBOSE` to nil. (Nobuyoshi Nakada, #1414)
104
+ * Support keyword argument semantics when constraining argument expectations using
105
+ `with` on Ruby 3.0+ (Yusuke Endoh, #1394)
106
+
107
+ ### 3.10.2 / 2021-01-27
108
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.1...v3.10.2)
109
+
110
+ Bug Fixes:
111
+
112
+ * Support keyword arguments with `and_call_original` on Ruby 3.0.
113
+ (Bryan Powell, #1385)
114
+ * `RSpec::Mocks::Constant#previously_defined?` is now always a boolean.
115
+ (Phil Pirozhkov, #1397)
116
+ * Support keyword arguments on Ruby 3.0 when used with `expect_any_instance_of`
117
+ or `allow_any_instance_of` with `and_call_original`.
118
+ (Jess Hottenstein, #1407)
119
+
120
+ ### 3.10.1 / 2020-12-27
121
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.0...v3.10.1)
122
+
123
+ Bug Fixes:
124
+
125
+ * Issue `ArgumentError` rather than `TypeError` when unsupported methods on
126
+ unsupported objects are attempted to be stubbed. (@zhisme, #1357)
127
+
128
+ ### 3.10.0 / 2020-10-30
129
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.1...v3.10.0)
130
+
131
+ Enhancements:
132
+ * Add the ability to set a custom error generator in `MessageExpectation`.
133
+ This will allow rspec-expectations to inject a custom failure message.
134
+ (Benoit Tigeot and Nicolas Zermati, #1312)
135
+ * Return the result of the block passed to `RSpec::Mocks.with_temporary_scope`
136
+ when block run. (@expeehaa, #1329)
137
+
138
+ ### 3.9.1 / 2019-12-31
139
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.0...v3.9.1)
140
+
141
+ Bug Fixes:
142
+
143
+ * Trigger `RSpec::Mocks.configuration.verifying_double_callbacks` when using
144
+ `allow_any_instance_of` or `expect_any_instance_of` (Daniel Orner, #1309)
145
+
146
+ ### 3.9.0 / 2019-10-07
147
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.2...v3.9.0)
148
+
149
+ Enhancements:
150
+
151
+ * Improve thread safety of message expectations by using Mutex to prevent
152
+ deadlocking errors. (Ry Biesemeyer, #1236)
153
+ * Add the ability to use `time` as an alias for `times`. For example:
154
+ `expect(Class).to receive(:method).exactly(1).time`.
155
+ (Pistos, Benoit Tigeot, #1271)
156
+
157
+ ### 3.8.2 / 2019-10-02
158
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.1...v3.8.2)
159
+
160
+ * Allow `array_including` argument matchers to be nested.
161
+ (Emmanuel Delmas, #1291)
162
+
163
+ ### 3.8.1 / 2019-06-13
164
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.0...v3.8.1)
165
+
166
+ Bug Fixes:
167
+
168
+ * Ensure stubbing methods does not change their visibility.
169
+ (Kevin Boschert, #1277)
170
+
1
171
  ### 3.8.0 / 2018-08-04
2
172
  [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...v3.8.0)
3
173
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.svg?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.svg)](https://codeclimate.com/github/rspec/rspec-mocks)
1
+ # RSpec Mocks [![Build Status](https://github.com/rspec/rspec-mocks/workflows/RSpec%20CI/badge.svg)](https://github.com/rspec/rspec-mocks/actions) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.svg)](https://codeclimate.com/github/rspec/rspec-mocks)
2
2
  rspec-mocks is a test-double framework for rspec with support for method stubs,
3
3
  fakes, and message expectations on generated test-doubles and real objects
4
4
  alike.
@@ -8,12 +8,12 @@ alike.
8
8
  gem install rspec # for rspec-core, rspec-expectations, rspec-mocks
9
9
  gem install rspec-mocks # for rspec-mocks only
10
10
 
11
- Want to run against the `master` branch? You'll need to include the dependent
11
+ Want to run against the `main` branch? You'll need to include the dependent
12
12
  RSpec repos as well. Add the following to your `Gemfile`:
13
13
 
14
14
  ```ruby
15
15
  %w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|
16
- gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'master'
16
+ gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'main'
17
17
  end
18
18
  ```
19
19
  ## Contributing
@@ -53,7 +53,7 @@ book = instance_double("Book", :pages => 250)
53
53
  Verifying doubles have some clever tricks to enable you to both test in
54
54
  isolation without your dependencies loaded while still being able to validate
55
55
  them against real objects. More detail is available in [their
56
- documentation](https://github.com/rspec/rspec-mocks/blob/master/features/verifying_doubles).
56
+ documentation](https://github.com/rspec/rspec-mocks/blob/main/features/verifying_doubles).
57
57
 
58
58
  Verifying doubles can also accept custom identifiers, just like double(), e.g.:
59
59
 
@@ -285,12 +285,15 @@ expect(double).to receive(:msg).with(hash_excluding(:a => 5)) # first arg is a h
285
285
  ```ruby
286
286
  expect(double).to receive(:msg).once
287
287
  expect(double).to receive(:msg).twice
288
+ expect(double).to receive(:msg).exactly(n).time
288
289
  expect(double).to receive(:msg).exactly(n).times
289
290
  expect(double).to receive(:msg).at_least(:once)
290
291
  expect(double).to receive(:msg).at_least(:twice)
292
+ expect(double).to receive(:msg).at_least(n).time
291
293
  expect(double).to receive(:msg).at_least(n).times
292
294
  expect(double).to receive(:msg).at_most(:once)
293
295
  expect(double).to receive(:msg).at_most(:twice)
296
+ expect(double).to receive(:msg).at_most(n).time
294
297
  expect(double).to receive(:msg).at_most(n).times
295
298
  ```
296
299
 
@@ -327,7 +330,7 @@ expect(double).to receive(:msg).and_return(value)
327
330
  expect(double).to receive(:msg).exactly(3).times.and_return(value1, value2, value3)
328
331
  # returns value1 the first time, value2 the second, etc
329
332
  expect(double).to receive(:msg).and_raise(error)
330
- # error can be an instantiated object or a class
333
+ # `error` can be an instantiated object (e.g. `StandardError.new(some_arg)`) or a class (e.g. `StandardError`)
331
334
  # if it is a class, it must be instantiable with no args
332
335
  expect(double).to receive(:msg).and_throw(:msg)
333
336
  expect(double).to receive(:msg).and_yield(values, to, yield)
@@ -400,7 +403,7 @@ your code.
400
403
  ## Stubbing and Hiding Constants
401
404
 
402
405
  See the [mutating constants
403
- README](https://github.com/rspec/rspec-mocks/blob/master/features/mutating_constants/README.md)
406
+ README](https://github.com/rspec/rspec-mocks/blob/main/features/mutating_constants/README.md)
404
407
  for info on this feature.
405
408
 
406
409
  ## Use `before(:example)`, not `before(:context)`
@@ -41,6 +41,7 @@ module RSpec
41
41
  record :thrice
42
42
  record :exactly
43
43
  record :times
44
+ record :time
44
45
  record :never
45
46
  record :at_least
46
47
  record :at_most
@@ -49,7 +49,7 @@ module RSpec
49
49
  # @private
50
50
  def unfulfilled_expectations
51
51
  @chains_by_method_name.map do |method_name, chains|
52
- method_name.to_s if ExpectationChain === chains.last unless chains.last.expectation_fulfilled?
52
+ method_name.to_s if ExpectationChain === chains.last && !chains.last.expectation_fulfilled?
53
53
  end.compact
54
54
  end
55
55
 
@@ -10,7 +10,7 @@ module RSpec
10
10
  #
11
11
  # This proxy sits in front of the recorder and delegates both to it
12
12
  # and to the `RSpec::Mocks::Proxy` for each already mocked or stubbed
13
- # instance of the class, in order to propogates changes to the instances.
13
+ # instance of the class, in order to propagates changes to the instances.
14
14
  #
15
15
  # Note that unlike `RSpec::Mocks::Proxy`, this proxy class is stateless
16
16
  # and is not persisted in `RSpec::Mocks.space`.
@@ -83,6 +83,15 @@ module RSpec
83
83
  end
84
84
  end
85
85
 
86
+ unless defined?(BasicObject)
87
+ class BasicObject
88
+ # Remove all methods except those expected to be defined on BasicObject
89
+ (instance_methods.map(&:to_sym) - [:__send__, :"!", :instance_eval, :==, :instance_exec, :"!=", :equal?, :__id__, :__binding__, :object_id]).each do |method|
90
+ undef_method method
91
+ end
92
+ end
93
+ end
94
+
86
95
  # @private
87
96
  # Delegates messages to each of the given targets in order to
88
97
  # provide the fluent interface that is available off of message
@@ -91,12 +100,12 @@ module RSpec
91
100
  # `targets` will typically contain 1 of the `AnyInstance::Recorder`
92
101
  # return values and N `MessageExpectation` instances (one per instance
93
102
  # of the `any_instance` klass).
94
- class FluentInterfaceProxy
103
+ class FluentInterfaceProxy < BasicObject
95
104
  def initialize(targets)
96
105
  @targets = targets
97
106
  end
98
107
 
99
- if RUBY_VERSION.to_f > 1.8
108
+ if ::RUBY_VERSION.to_f > 1.8
100
109
  def respond_to_missing?(method_name, include_private=false)
101
110
  super || @targets.first.respond_to?(method_name, include_private)
102
111
  end
@@ -21,6 +21,11 @@ module RSpec
21
21
  @backed_up_method_owner = {}
22
22
  @klass = klass
23
23
  @expectation_set = false
24
+
25
+ return unless RSpec::Mocks.configuration.verify_partial_doubles?
26
+ RSpec::Mocks.configuration.verifying_double_callbacks.each do |block|
27
+ block.call(ObjectReference.for(klass))
28
+ end
24
29
  end
25
30
 
26
31
  # Initializes the recording a stub to be played back against any
@@ -256,6 +261,7 @@ module RSpec
256
261
  recorder.playback!(self, method_name)
257
262
  __send__(method_name, *args, &blk)
258
263
  end
264
+ @klass.__send__(:ruby2_keywords, method_name) if @klass.respond_to?(:ruby2_keywords, true)
259
265
  end
260
266
 
261
267
  def mark_invoked!(method_name)
@@ -46,17 +46,34 @@ module RSpec
46
46
  @expected_args = expected_args
47
47
  ensure_expected_args_valid!
48
48
  end
49
+ ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
49
50
 
50
51
  # @api public
51
- # @param [Array] args
52
+ # @param [Array] actual_args
52
53
  #
53
54
  # Matches each element in the `expected_args` against the element in the same
54
55
  # position of the arguments passed to `new`.
55
56
  #
56
57
  # @see #initialize
57
- def args_match?(*args)
58
- Support::FuzzyMatcher.values_match?(resolve_expected_args_based_on(args), args)
58
+ def args_match?(*actual_args)
59
+ expected_args = resolve_expected_args_based_on(actual_args)
60
+
61
+ return false if expected_args.size != actual_args.size
62
+
63
+ if RUBY_VERSION >= "3"
64
+ # If the expectation was set with keywords, while the actual method was called with a positional hash argument, they don't match.
65
+ # If the expectation was set without keywords, e.g., with({a: 1}), then it fine to call it with either foo(a: 1) or foo({a: 1}).
66
+ # This corresponds to Ruby semantics, as if the method was def foo(options).
67
+ if Hash === expected_args.last && Hash === actual_args.last
68
+ if !Hash.ruby2_keywords_hash?(actual_args.last) && Hash.ruby2_keywords_hash?(expected_args.last)
69
+ return false
70
+ end
71
+ end
72
+ end
73
+
74
+ Support::FuzzyMatcher.values_match?(expected_args, actual_args)
59
75
  end
76
+ ruby2_keywords :args_match? if respond_to?(:ruby2_keywords, true)
60
77
 
61
78
  # @private
62
79
  # Resolves abstract arg placeholders like `no_args` and `any_args` into
@@ -241,6 +241,8 @@ module RSpec
241
241
  RSpec::Support::FuzzyMatcher.values_match?(expected_element, actual_element)
242
242
  end
243
243
  end
244
+ rescue NoMethodError
245
+ false
244
246
  end
245
247
 
246
248
  def description
@@ -268,11 +268,32 @@ module RSpec
268
268
  def error_message(expectation, args_for_multiple_calls)
269
269
  expected_args = format_args(expectation.expected_args)
270
270
  actual_args = format_received_args(args_for_multiple_calls)
271
+
272
+ if RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash?
273
+ expected_hash = expectation.expected_args.last
274
+ actual_hash = args_for_multiple_calls.last.last
275
+ if Hash === expected_hash && Hash === actual_hash &&
276
+ (Hash.ruby2_keywords_hash?(expected_hash) != Hash.ruby2_keywords_hash?(actual_hash))
277
+
278
+ actual_description = Hash.ruby2_keywords_hash?(actual_hash) ? " (keyword arguments)" : " (options hash)"
279
+ expected_description = Hash.ruby2_keywords_hash?(expected_hash) ? " (keyword arguments)" : " (options hash)"
280
+
281
+ if actual_description != expected_description
282
+ actual_args += actual_description
283
+ expected_args += expected_description
284
+ end
285
+ end
286
+ end
287
+
271
288
  message = default_error_message(expectation, expected_args, actual_args)
272
289
 
273
290
  if args_for_multiple_calls.one?
274
291
  diff = diff_message(expectation.expected_args, args_for_multiple_calls.first)
275
- message << "\nDiff:#{diff}" unless diff.strip.empty?
292
+ if RSpec::Mocks.configuration.color?
293
+ message << "\nDiff:#{diff}" unless diff.gsub(/\e\[\d+m/, '').strip.empty?
294
+ else
295
+ message << "\nDiff:#{diff}" unless diff.strip.empty?
296
+ end
276
297
  end
277
298
 
278
299
  message
@@ -5,7 +5,7 @@ module RSpec
5
5
  class HaveReceived
6
6
  include Matcher
7
7
 
8
- COUNT_CONSTRAINTS = %w[exactly at_least at_most times once twice thrice]
8
+ COUNT_CONSTRAINTS = %w[exactly at_least at_most times time once twice thrice]
9
9
  ARGS_CONSTRAINTS = %w[with]
10
10
  CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w[ordered]
11
11
 
@@ -16,7 +16,7 @@ module RSpec
16
16
  @subject = nil
17
17
  end
18
18
 
19
- def name
19
+ def matcher_name
20
20
  "have_received"
21
21
  end
22
22
 
@@ -13,7 +13,7 @@ module RSpec
13
13
  @recorded_customizations = []
14
14
  end
15
15
 
16
- def name
16
+ def matcher_name
17
17
  "receive"
18
18
  end
19
19
 
@@ -55,13 +55,15 @@ module RSpec
55
55
  setup_any_instance_method_substitute(subject, :stub, block)
56
56
  end
57
57
 
58
+ own_methods = (instance_methods - superclass.instance_methods)
58
59
  MessageExpectation.public_instance_methods(false).each do |method|
59
- next if method_defined?(method)
60
+ next if own_methods.include?(method)
60
61
 
61
62
  define_method(method) do |*args, &block|
62
63
  @recorded_customizations << ExpectationCustomization.new(method, args, block)
63
64
  self
64
65
  end
66
+ ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
65
67
  end
66
68
 
67
69
  private
@@ -13,14 +13,14 @@ module RSpec
13
13
  @recorded_customizations = []
14
14
  end
15
15
 
16
- [:with, :and_return, :and_throw, :and_raise, :and_yield, :and_call_original].each do |msg|
16
+ [:with, :and_return, :and_invoke, :and_throw, :and_raise, :and_yield, :and_call_original].each do |msg|
17
17
  define_method(msg) do |*args, &block|
18
18
  @recorded_customizations << ExpectationCustomization.new(msg, args, block)
19
19
  self
20
20
  end
21
21
  end
22
22
 
23
- def name
23
+ def matcher_name
24
24
  "receive_message_chain"
25
25
  end
26
26
 
@@ -10,7 +10,7 @@ module RSpec
10
10
  @backtrace_line = CallerFilter.first_non_rspec_line
11
11
  end
12
12
 
13
- def name
13
+ def matcher_name
14
14
  "receive_messages"
15
15
  end
16
16
 
@@ -1,3 +1,5 @@
1
+ RSpec::Support.require_rspec_support 'mutex'
2
+
1
3
  module RSpec
2
4
  module Mocks
3
5
  # A message expectation that only allows concrete return values to be set
@@ -51,7 +53,7 @@ module RSpec
51
53
  # etc.
52
54
  #
53
55
  # If the message is received more times than there are values, the last
54
- # value is received for every subsequent call.
56
+ # value is returned for every subsequent call.
55
57
  #
56
58
  # @return [nil] No further chaining is supported after this.
57
59
  # @example
@@ -83,6 +85,48 @@ module RSpec
83
85
  nil
84
86
  end
85
87
 
88
+ # Tells the object to invoke a Proc when it receives the message. Given
89
+ # more than one value, the result of the first Proc is returned the first
90
+ # time the message is received, the result of the second Proc is returned
91
+ # the next time, etc, etc.
92
+ #
93
+ # If the message is received more times than there are Procs, the result of
94
+ # the last Proc is returned for every subsequent call.
95
+ #
96
+ # @return [nil] No further chaining is supported after this.
97
+ # @example
98
+ # allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout })
99
+ # api.get_foo # => raises ApiTimeout
100
+ # api.get_foo # => raises ApiTimeout
101
+ #
102
+ # allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout }, -> { raise ApiTimeout }, -> { :a_foo })
103
+ # api.get_foo # => raises ApiTimeout
104
+ # api.get_foo # => rasies ApiTimeout
105
+ # api.get_foo # => :a_foo
106
+ # api.get_foo # => :a_foo
107
+ # api.get_foo # => :a_foo
108
+ # # etc
109
+ def and_invoke(first_proc, *procs)
110
+ raise_already_invoked_error_if_necessary(__method__)
111
+ if negative?
112
+ raise "`and_invoke` is not supported with negative message expectations"
113
+ end
114
+
115
+ if block_given?
116
+ raise ArgumentError, "Implementation blocks aren't supported with `and_invoke`"
117
+ end
118
+
119
+ procs.unshift(first_proc)
120
+ if procs.any? { |p| !p.respond_to?(:call) }
121
+ raise ArgumentError, "Arguments to `and_invoke` must be callable."
122
+ end
123
+
124
+ @expected_received_count = [@expected_received_count, procs.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
125
+ self.terminal_implementation_action = AndInvokeImplementation.new(procs)
126
+
127
+ nil
128
+ end
129
+
86
130
  # Tells the object to delegate to the original unmodified method
87
131
  # when it receives the message.
88
132
  #
@@ -95,9 +139,12 @@ module RSpec
95
139
  # counter.increment
96
140
  # expect(counter.count).to eq(original_count + 1)
97
141
  def and_call_original
98
- wrap_original(__method__) do |original, *args, &block|
99
- original.call(*args, &block)
142
+ block = lambda do |original, *args, &b|
143
+ original.call(*args, &b)
100
144
  end
145
+ block = block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
146
+
147
+ wrap_original(__method__, &block)
101
148
  end
102
149
 
103
150
  # Decorates the stubbed method with the supplied block. The original
@@ -236,6 +283,7 @@ module RSpec
236
283
  self.inner_implementation_action = block
237
284
  self
238
285
  end
286
+ alias time times
239
287
 
240
288
  # Expect a message not to be received at all.
241
289
  #
@@ -319,6 +367,7 @@ module RSpec
319
367
  @argument_list_matcher = ArgumentListMatcher.new(*args)
320
368
  self
321
369
  end
370
+ ruby2_keywords(:with) if respond_to?(:ruby2_keywords, true)
322
371
 
323
372
  # Expect messages to be received in a specific order.
324
373
  #
@@ -362,7 +411,7 @@ module RSpec
362
411
  attr_reader :message
363
412
  attr_reader :orig_object
364
413
  attr_writer :expected_received_count, :expected_from, :argument_list_matcher
365
- protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
414
+ protected :expected_received_count=, :expected_from=, :error_generator=, :implementation=
366
415
 
367
416
  # @private
368
417
  attr_reader :type
@@ -372,12 +421,13 @@ module RSpec
372
421
  type=:expectation, opts={}, &implementation_block)
373
422
  @type = type
374
423
  @error_generator = error_generator
375
- @error_generator.opts = opts
424
+ @error_generator.opts = error_generator.opts.merge(opts)
376
425
  @expected_from = expected_from
377
426
  @method_double = method_double
378
427
  @orig_object = @method_double.object
379
428
  @message = @method_double.method_name
380
429
  @actual_received_count = 0
430
+ @actual_received_count_write_mutex = Support::Mutex.new
381
431
  @expected_received_count = type == :expectation ? 1 : :any
382
432
  @argument_list_matcher = ArgumentListMatcher::MATCH_ALL
383
433
  @order_group = expectation_ordering
@@ -413,18 +463,22 @@ module RSpec
413
463
  def matches?(message, *args)
414
464
  @message == message && @argument_list_matcher.args_match?(*args)
415
465
  end
466
+ ruby2_keywords :matches? if respond_to?(:ruby2_keywords, true)
416
467
 
417
468
  def safe_invoke(parent_stub, *args, &block)
418
469
  invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block)
419
470
  end
471
+ ruby2_keywords :safe_invoke if respond_to?(:ruby2_keywords, true)
420
472
 
421
473
  def invoke(parent_stub, *args, &block)
422
474
  invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block)
423
475
  end
476
+ ruby2_keywords :invoke if respond_to?(:ruby2_keywords, true)
424
477
 
425
478
  def invoke_without_incrementing_received_count(parent_stub, *args, &block)
426
479
  invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block)
427
480
  end
481
+ ruby2_keywords :invoke_without_incrementing_received_count if respond_to?(:ruby2_keywords, true)
428
482
 
429
483
  def negative?
430
484
  @expected_received_count == 0 && !@at_least
@@ -536,7 +590,9 @@ module RSpec
536
590
  end
537
591
 
538
592
  def increase_actual_received_count!
539
- @actual_received_count += 1
593
+ @actual_received_count_write_mutex.synchronize do
594
+ @actual_received_count += 1
595
+ end
540
596
  end
541
597
 
542
598
  private
@@ -567,8 +623,11 @@ module RSpec
567
623
  parent_stub.invoke(nil, *args, &block)
568
624
  end
569
625
  ensure
570
- @actual_received_count += increment
626
+ @actual_received_count_write_mutex.synchronize do
627
+ @actual_received_count += increment
628
+ end
571
629
  end
630
+ ruby2_keywords :invoke_incrementing_actual_calls_by if respond_to?(:ruby2_keywords, true)
572
631
 
573
632
  def has_been_invoked?
574
633
  @actual_received_count > 0
@@ -673,6 +732,24 @@ module RSpec
673
732
  end
674
733
  end
675
734
 
735
+ # Handles the implementation of an `and_invoke` implementation.
736
+ # @private
737
+ class AndInvokeImplementation
738
+ def initialize(procs_to_invoke)
739
+ @procs_to_invoke = procs_to_invoke
740
+ end
741
+
742
+ def call(*args, &block)
743
+ proc = if @procs_to_invoke.size > 1
744
+ @procs_to_invoke.shift
745
+ else
746
+ @procs_to_invoke.first
747
+ end
748
+
749
+ proc.call(*args, &block)
750
+ end
751
+ end
752
+
676
753
  # Represents a configured implementation. Takes into account
677
754
  # any number of sub-implementations.
678
755
  # @private
@@ -684,6 +761,7 @@ module RSpec
684
761
  action.call(*args, &block)
685
762
  end.last
686
763
  end
764
+ ruby2_keywords :call if respond_to?(:ruby2_keywords, true)
687
765
 
688
766
  def present?
689
767
  actions.any?
@@ -729,6 +807,7 @@ module RSpec
729
807
  def call(*args, &block)
730
808
  @block.call(@method, *args, &block)
731
809
  end
810
+ ruby2_keywords :call if respond_to?(:ruby2_keywords, true)
732
811
 
733
812
  private
734
813
 
@@ -2,6 +2,9 @@ module RSpec
2
2
  module Mocks
3
3
  # @private
4
4
  class MethodDouble
5
+ # @private TODO: drop in favor of FrozenError in ruby 2.5+
6
+ FROZEN_ERROR_MSG = /can't modify frozen/
7
+
5
8
  # @private
6
9
  attr_reader :method_name, :object, :expectations, :stubs, :method_stasher
7
10
 
@@ -23,10 +26,7 @@ module RSpec
23
26
  # handler of the object. This accounts for cases where the user has not
24
27
  # correctly defined `respond_to?`, and also 1.8 which does not provide
25
28
  # method handles for missing methods even if `respond_to?` is correct.
26
- @original_implementation_callable ||= original_method ||
27
- Proc.new do |*args, &block|
28
- @object.__send__(:method_missing, @method_name, *args, &block)
29
- end
29
+ @original_implementation_callable ||= original_method || method_missing_block
30
30
  end
31
31
 
32
32
  alias_method :save_original_implementation_callable!, :original_implementation_callable
@@ -37,6 +37,16 @@ module RSpec
37
37
  @proxy.original_method_handle_for(method_name)
38
38
  end
39
39
 
40
+ # @private
41
+ def method_missing_block
42
+ block = Proc.new do |*args, &b|
43
+ @object.__send__(:method_missing, @method_name, *args, &b)
44
+ end
45
+ block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
46
+
47
+ block
48
+ end
49
+
40
50
  # @private
41
51
  def visibility
42
52
  @proxy.visibility_for(@method_name)
@@ -59,14 +69,25 @@ module RSpec
59
69
  return if @method_is_proxied
60
70
 
61
71
  save_original_implementation_callable!
62
- definition_target.class_exec(self, method_name, visibility) do |method_double, method_name, visibility|
72
+ definition_target.class_exec(self, method_name, @original_visibility || visibility) do |method_double, method_name, visibility|
63
73
  define_method(method_name) do |*args, &block|
64
74
  method_double.proxy_method_invoked(self, *args, &block)
65
75
  end
76
+ # This can't be `if respond_to?(:ruby2_keywords, true)`,
77
+ # see https://github.com/rspec/rspec-mocks/pull/1385#issuecomment-755340298
78
+ ruby2_keywords(method_name) if Module.private_method_defined?(:ruby2_keywords)
66
79
  __send__(visibility, method_name)
67
80
  end
68
81
 
69
82
  @method_is_proxied = true
83
+ rescue RuntimeError, TypeError => e
84
+ # TODO: drop in favor of FrozenError in ruby 2.5+
85
+ # RuntimeError (and FrozenError) for ruby 2.x
86
+ # TypeError for ruby 1.x
87
+ if (defined?(FrozenError) && e.is_a?(FrozenError)) || FROZEN_ERROR_MSG === e.message
88
+ raise ArgumentError, "Cannot proxy frozen objects, rspec-mocks relies on proxies for method stubbing and expectations."
89
+ end
90
+ raise
70
91
  end
71
92
 
72
93
  # The implementation of the proxied method. Subclasses may override this
@@ -76,10 +97,10 @@ module RSpec
76
97
  def proxy_method_invoked(_obj, *args, &block)
77
98
  @proxy.message_received method_name, *args, &block
78
99
  end
100
+ ruby2_keywords :proxy_method_invoked if respond_to?(:ruby2_keywords, true)
79
101
 
80
102
  # @private
81
103
  def restore_original_method
82
- return show_frozen_warning if object_singleton_class.frozen?
83
104
  return unless @method_is_proxied
84
105
 
85
106
  remove_method_from_definition_target
@@ -87,6 +108,14 @@ module RSpec
87
108
  restore_original_visibility
88
109
 
89
110
  @method_is_proxied = false
111
+ rescue RuntimeError, TypeError => e
112
+ # TODO: drop in favor of FrozenError in ruby 2.5+
113
+ # RuntimeError (and FrozenError) for ruby 2.x
114
+ # TypeError for ruby 1.x
115
+ if (defined?(FrozenError) && e.is_a?(FrozenError)) || FROZEN_ERROR_MSG === e.message
116
+ return show_frozen_warning
117
+ end
118
+ raise
90
119
  end
91
120
 
92
121
  # @private
@@ -185,11 +185,23 @@ module RSpec
185
185
  def self.applies_to?(method_name)
186
186
  return false unless method_name == :new
187
187
  klass = yield
188
- return false unless klass.respond_to?(:new, true)
188
+ return false unless ::Class === klass && klass.respond_to?(:new, true)
189
189
 
190
190
  # We only want to apply our special logic to normal `new` methods.
191
191
  # Methods that the user has monkeyed with should be left as-is.
192
- ::RSpec::Support.method_handle_for(klass, :new).owner == ::Class
192
+ uses_class_new?(klass)
193
+ end
194
+
195
+ if RUBY_VERSION.to_i >= 3
196
+ CLASS_NEW = ::Class.instance_method(:new)
197
+
198
+ def self.uses_class_new?(klass)
199
+ ::RSpec::Support.method_handle_for(klass, :new) == CLASS_NEW.bind(klass)
200
+ end
201
+ else # Ruby 2's Method#== is too strict
202
+ def self.uses_class_new?(klass)
203
+ ::RSpec::Support.method_handle_for(klass, :new).owner == ::Class
204
+ end
193
205
  end
194
206
 
195
207
  def with_signature
@@ -37,7 +37,7 @@ if defined?(::Minitest::Expectation)
37
37
  # not want to here (or else we would interfere with rspec-expectations' definition).
38
38
  else
39
39
  # ...otherwise, define those methods now. If `rspec/expectations/minitest_integration`
40
- # is loaded after this file, it'll overide the defintion here.
40
+ # is loaded after this file, it'll override the definition here.
41
41
  Minitest::Expectation.class_eval do
42
42
  include RSpec::Mocks::ExpectationTargetMethods
43
43
 
@@ -66,7 +66,7 @@ module RSpec
66
66
 
67
67
  # @private
68
68
  def self.unmutated(name)
69
- previously_defined = recursive_const_defined?(name)
69
+ previously_defined = !!recursive_const_defined?(name)
70
70
  rescue NameError
71
71
  new(name) do |c|
72
72
  c.valid_name = false
@@ -81,7 +81,7 @@ module RSpec
81
81
  # Queries rspec-mocks to find out information about the named constant.
82
82
  #
83
83
  # @param [String] name the name of the constant
84
- # @return [Constant] an object contaning information about the named
84
+ # @return [Constant] an object containing information about the named
85
85
  # constant.
86
86
  def self.original(name)
87
87
  mutator = ::RSpec::Mocks.space.constant_mutator_for(name)
@@ -9,6 +9,11 @@ module RSpec
9
9
  end
10
10
  end
11
11
 
12
+ unless defined?(Mutex)
13
+ Support.require_rspec_support 'mutex'
14
+ Mutex = Support::Mutex
15
+ end
16
+
12
17
  # @private
13
18
  def ensure_implemented(*_args)
14
19
  # noop for basic proxies, see VerifyingProxy for behaviour.
@@ -16,15 +21,26 @@ module RSpec
16
21
 
17
22
  # @private
18
23
  def initialize(object, order_group, options={})
24
+ ensure_can_be_proxied!(object)
25
+
19
26
  @object = object
20
27
  @order_group = order_group
21
28
  @error_generator = ErrorGenerator.new(object)
22
29
  @messages_received = []
30
+ @messages_received_mutex = Mutex.new
23
31
  @options = options
24
32
  @null_object = false
25
33
  @method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) }
26
34
  end
27
35
 
36
+ # @private
37
+ def ensure_can_be_proxied!(object)
38
+ return unless object.is_a?(Symbol)
39
+
40
+ msg = "Cannot proxy frozen objects. Symbols such as #{object} cannot be mocked or stubbed."
41
+ raise ArgumentError, msg
42
+ end
43
+
28
44
  # @private
29
45
  attr_reader :object
30
46
 
@@ -90,27 +106,31 @@ module RSpec
90
106
  @error_generator.raise_expectation_on_unstubbed_method(expected_method_name)
91
107
  end
92
108
 
93
- @messages_received.each do |(actual_method_name, args, received_block)|
94
- next unless expectation.matches?(actual_method_name, *args)
109
+ @messages_received_mutex.synchronize do
110
+ @messages_received.each do |(actual_method_name, args, received_block)|
111
+ next unless expectation.matches?(actual_method_name, *args)
95
112
 
96
- expectation.safe_invoke(nil)
97
- block.call(*args, &received_block) if block
113
+ expectation.safe_invoke(nil)
114
+ block.call(*args, &received_block) if block
115
+ end
98
116
  end
99
117
  end
100
118
 
101
119
  # @private
102
120
  def check_for_unexpected_arguments(expectation)
103
- return if @messages_received.empty?
121
+ @messages_received_mutex.synchronize do
122
+ return if @messages_received.empty?
104
123
 
105
- return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) }
124
+ return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) }
106
125
 
107
- name_but_not_args, others = @messages_received.partition do |(method_name, args, _)|
108
- expectation.matches_name_but_not_args(method_name, *args)
109
- end
126
+ name_but_not_args, others = @messages_received.partition do |(method_name, args, _)|
127
+ expectation.matches_name_but_not_args(method_name, *args)
128
+ end
110
129
 
111
- return if name_but_not_args.empty? && !others.empty?
130
+ return if name_but_not_args.empty? && !others.empty?
112
131
 
113
- expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] })
132
+ expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] })
133
+ end
114
134
  end
115
135
 
116
136
  # @private
@@ -141,17 +161,23 @@ module RSpec
141
161
 
142
162
  # @private
143
163
  def reset
144
- @messages_received.clear
164
+ @messages_received_mutex.synchronize do
165
+ @messages_received.clear
166
+ end
145
167
  end
146
168
 
147
169
  # @private
148
170
  def received_message?(method_name, *args, &block)
149
- @messages_received.any? { |array| array == [method_name, args, block] }
171
+ @messages_received_mutex.synchronize do
172
+ @messages_received.any? { |array| array == [method_name, args, block] }
173
+ end
150
174
  end
151
175
 
152
176
  # @private
153
177
  def messages_arg_list
154
- @messages_received.map { |_, args, _| args }
178
+ @messages_received_mutex.synchronize do
179
+ @messages_received.map { |_, args, _| args }
180
+ end
155
181
  end
156
182
 
157
183
  # @private
@@ -162,8 +188,11 @@ module RSpec
162
188
  # @private
163
189
  def record_message_received(message, *args, &block)
164
190
  @order_group.invoked SpecificMessage.new(object, message, args)
165
- @messages_received << [message, args, block]
191
+ @messages_received_mutex.synchronize do
192
+ @messages_received << [message, args, block]
193
+ end
166
194
  end
195
+ ruby2_keywords :record_message_received if respond_to?(:ruby2_keywords, true)
167
196
 
168
197
  # @private
169
198
  def message_received(message, *args, &block)
@@ -196,6 +225,7 @@ module RSpec
196
225
  @object.__send__(:method_missing, message, *args, &block)
197
226
  end
198
227
  end
228
+ ruby2_keywords :message_received if respond_to?(:ruby2_keywords, true)
199
229
 
200
230
  # @private
201
231
  def raise_unexpected_message_error(method_name, args)
@@ -245,12 +275,14 @@ module RSpec
245
275
  expectation.matches?(method_name, *args)
246
276
  end
247
277
  end
278
+ ruby2_keywords :find_matching_expectation if respond_to?(:ruby2_keywords, true)
248
279
 
249
280
  def find_almost_matching_expectation(method_name, *args)
250
281
  find_best_matching_expectation_for(method_name) do |expectation|
251
282
  expectation.matches_name_but_not_args(method_name, *args)
252
283
  end
253
284
  end
285
+ ruby2_keywords :find_almost_matching_expectation if respond_to?(:ruby2_keywords, true)
254
286
 
255
287
  def find_best_matching_expectation_for(method_name)
256
288
  first_match = nil
@@ -267,10 +299,12 @@ module RSpec
267
299
  def find_matching_method_stub(method_name, *args)
268
300
  method_double_for(method_name).stubs.find { |stub| stub.matches?(method_name, *args) }
269
301
  end
302
+ ruby2_keywords :find_matching_method_stub if respond_to?(:ruby2_keywords, true)
270
303
 
271
304
  def find_almost_matching_stub(method_name, *args)
272
305
  method_double_for(method_name).stubs.find { |stub| stub.matches_name_but_not_args(method_name, *args) }
273
306
  end
307
+ ruby2_keywords :find_almost_matching_stub if respond_to?(:ruby2_keywords, true)
274
308
  end
275
309
 
276
310
  # @private
@@ -326,6 +360,7 @@ module RSpec
326
360
  end
327
361
  super
328
362
  end
363
+ ruby2_keywords :message_received if respond_to?(:ruby2_keywords, true)
329
364
 
330
365
  private
331
366
 
@@ -77,9 +77,9 @@ module RSpec
77
77
 
78
78
  def reset_all
79
79
  proxies.each_value { |proxy| proxy.reset }
80
- @constant_mutators.reverse.each { |mut| mut.idempotently_reset }
81
80
  any_instance_recorders.each_value { |recorder| recorder.stop_all_observation! }
82
81
  any_instance_recorders.clear
82
+ @constant_mutators.reverse.each { |mut| mut.idempotently_reset }
83
83
  end
84
84
 
85
85
  def register_constant_mutator(mutator)
@@ -54,7 +54,7 @@ module RSpec
54
54
 
55
55
  def raise_negation_unsupported(method_name, matcher)
56
56
  raise NegationUnsupportedError,
57
- "`#{expression}(...).#{method_name} #{matcher.name}` is not supported since it " \
57
+ "`#{expression}(...).#{method_name} #{matcher.matcher_name}` is not supported since it " \
58
58
  "doesn't really make sense. What would it even mean?"
59
59
  end
60
60
  end
@@ -34,27 +34,21 @@ module RSpec
34
34
  super
35
35
  end
36
36
 
37
- # @private
38
- module SilentIO
39
- def self.method_missing(*); end
40
- def self.respond_to?(*)
41
- true
42
- end
43
- end
44
-
45
37
  # Redefining `__send__` causes ruby to issue a warning.
46
- old, $stderr = $stderr, SilentIO
38
+ old, $VERBOSE = $VERBOSE, nil
47
39
  def __send__(name, *args, &block)
48
40
  @__sending_message = name
49
41
  super
50
42
  ensure
51
43
  @__sending_message = nil
52
44
  end
53
- $stderr = old
45
+ ruby2_keywords :__send__ if respond_to?(:ruby2_keywords, true)
46
+ $VERBOSE = old
54
47
 
55
48
  def send(name, *args, &block)
56
49
  __send__(name, *args, &block)
57
50
  end
51
+ ruby2_keywords :send if respond_to?(:ruby2_keywords, true)
58
52
 
59
53
  def initialize(doubled_module, *args)
60
54
  @doubled_module = doubled_module
@@ -31,6 +31,7 @@ module RSpec
31
31
  end
32
32
  end
33
33
  end
34
+ ruby2_keywords(:with) if respond_to?(:ruby2_keywords, true)
34
35
 
35
36
  private
36
37
 
@@ -57,7 +57,7 @@ module RSpec
57
57
  # A verifying proxy mostly acts like a normal proxy, except that it
58
58
  # contains extra logic to try and determine the validity of any expectation
59
59
  # set on it. This includes whether or not methods have been defined and the
60
- # validatiy of arguments on method calls.
60
+ # validity of arguments on method calls.
61
61
  #
62
62
  # In all other ways this behaves like a normal proxy. It only adds the
63
63
  # verification behaviour to specific methods then delegates to the parent
@@ -147,12 +147,12 @@ module RSpec
147
147
  end
148
148
 
149
149
  def add_expectation(*args, &block)
150
- # explict params necessary for 1.8.7 see #626
150
+ # explicit params necessary for 1.8.7 see #626
151
151
  super(*args, &block).tap { |x| x.method_reference = @method_reference }
152
152
  end
153
153
 
154
154
  def add_stub(*args, &block)
155
- # explict params necessary for 1.8.7 see #626
155
+ # explicit params necessary for 1.8.7 see #626
156
156
  super(*args, &block).tap { |x| x.method_reference = @method_reference }
157
157
  end
158
158
 
@@ -160,6 +160,7 @@ module RSpec
160
160
  validate_arguments!(args)
161
161
  super
162
162
  end
163
+ ruby2_keywords :proxy_method_invoked if respond_to?(:ruby2_keywords, true)
163
164
 
164
165
  def validate_arguments!(actual_args)
165
166
  @method_reference.with_signature do |signature|
@@ -3,7 +3,7 @@ module RSpec
3
3
  # Version information for RSpec mocks.
4
4
  module Version
5
5
  # Version of RSpec mocks currently in use in SemVer format.
6
- STRING = '3.8.0'
6
+ STRING = '3.12.6'
7
7
  end
8
8
  end
9
9
  end
data/lib/rspec/mocks.rb CHANGED
@@ -87,12 +87,15 @@ module RSpec
87
87
 
88
88
  # Call the passed block and verify mocks after it has executed. This allows
89
89
  # mock usage in arbitrary places, such as a `before(:all)` hook.
90
+ #
91
+ # @return [Object] the return value from the block
90
92
  def self.with_temporary_scope
91
93
  setup
92
94
 
93
95
  begin
94
- yield
96
+ result = yield
95
97
  verify
98
+ result
96
99
  ensure
97
100
  teardown
98
101
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-mocks
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.8.0
4
+ version: 3.12.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Baker
8
8
  - David Chelimsky
9
9
  - Myron Marston
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain:
13
13
  - |
@@ -45,7 +45,7 @@ cert_chain:
45
45
  ZsVDj6a7lH3cNqtWXZxrb2wO38qV5AkYj8SQK7Hj3/Yui9myUX3crr+PdetazSqQ
46
46
  F3MdtaDehhjC
47
47
  -----END CERTIFICATE-----
48
- date: 2018-08-04 00:00:00.000000000 Z
48
+ date: 2023-07-11 00:00:00.000000000 Z
49
49
  dependencies:
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: rspec-support
@@ -53,14 +53,14 @@ dependencies:
53
53
  requirements:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
- version: 3.8.0
56
+ version: 3.12.0
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: 3.8.0
63
+ version: 3.12.0
64
64
  - !ruby/object:Gem::Dependency
65
65
  name: diff-lcs
66
66
  requirement: !ruby/object:Gem::Requirement
@@ -85,44 +85,44 @@ dependencies:
85
85
  name: rake
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - "~>"
88
+ - - ">"
89
89
  - !ruby/object:Gem::Version
90
90
  version: 10.0.0
91
91
  type: :development
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - "~>"
95
+ - - ">"
96
96
  - !ruby/object:Gem::Version
97
97
  version: 10.0.0
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: cucumber
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - "~>"
102
+ - - ">="
103
103
  - !ruby/object:Gem::Version
104
- version: 1.3.15
104
+ version: '1.3'
105
105
  type: :development
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - "~>"
109
+ - - ">="
110
110
  - !ruby/object:Gem::Version
111
- version: 1.3.15
111
+ version: '1.3'
112
112
  - !ruby/object:Gem::Dependency
113
113
  name: aruba
114
114
  requirement: !ruby/object:Gem::Requirement
115
115
  requirements:
116
116
  - - "~>"
117
117
  - !ruby/object:Gem::Version
118
- version: 0.6.2
118
+ version: '1.1'
119
119
  type: :development
120
120
  prerelease: false
121
121
  version_requirements: !ruby/object:Gem::Requirement
122
122
  requirements:
123
123
  - - "~>"
124
124
  - !ruby/object:Gem::Version
125
- version: 0.6.2
125
+ version: '1.1'
126
126
  - !ruby/object:Gem::Dependency
127
127
  name: minitest
128
128
  requirement: !ruby/object:Gem::Requirement
@@ -192,8 +192,13 @@ files:
192
192
  homepage: https://github.com/rspec/rspec-mocks
193
193
  licenses:
194
194
  - MIT
195
- metadata: {}
196
- post_install_message:
195
+ metadata:
196
+ bug_tracker_uri: https://github.com/rspec/rspec-mocks/issues
197
+ changelog_uri: https://github.com/rspec/rspec-mocks/blob/v3.12.6/Changelog.md
198
+ documentation_uri: https://rspec.info/documentation/
199
+ mailing_list_uri: https://groups.google.com/forum/#!forum/rspec
200
+ source_code_uri: https://github.com/rspec/rspec-mocks
201
+ post_install_message:
197
202
  rdoc_options:
198
203
  - "--charset=UTF-8"
199
204
  require_paths:
@@ -209,9 +214,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
214
  - !ruby/object:Gem::Version
210
215
  version: '0'
211
216
  requirements: []
212
- rubyforge_project:
213
- rubygems_version: 2.6.13
214
- signing_key:
217
+ rubygems_version: 3.4.1
218
+ signing_key:
215
219
  specification_version: 4
216
- summary: rspec-mocks-3.8.0
220
+ summary: rspec-mocks-3.12.6
217
221
  test_files: []
metadata.gz.sig CHANGED
Binary file