rspec-mocks 2.14.6 → 2.99.0.beta1
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 +8 -8
- data/Changelog.md +20 -14
- data/lib/rspec/mocks.rb +6 -2
- data/lib/rspec/mocks/any_instance/chain.rb +28 -0
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +5 -10
- data/lib/rspec/mocks/any_instance/recorder.rb +4 -1
- data/lib/rspec/mocks/any_instance/stub_chain.rb +5 -5
- data/lib/rspec/mocks/caller_filter.rb +55 -0
- data/lib/rspec/mocks/configuration.rb +19 -0
- data/lib/rspec/mocks/deprecation.rb +9 -1
- data/lib/rspec/mocks/extensions/proc.rb +63 -0
- data/lib/rspec/mocks/framework.rb +2 -0
- data/lib/rspec/mocks/matchers/receive.rb +2 -9
- data/lib/rspec/mocks/message_expectation.rb +92 -22
- data/lib/rspec/mocks/method_double.rb +2 -2
- data/lib/rspec/mocks/proxy_for_nil.rb +2 -2
- data/lib/rspec/mocks/space.rb +4 -5
- data/lib/rspec/mocks/stub_chain.rb +1 -1
- data/lib/rspec/mocks/syntax.rb +3 -3
- data/lib/rspec/mocks/targets.rb +1 -1
- data/lib/rspec/mocks/test_double.rb +8 -2
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/and_yield_spec.rb +1 -1
- data/spec/rspec/mocks/any_instance/message_chains_spec.rb +3 -3
- data/spec/rspec/mocks/any_instance_spec.rb +233 -84
- data/spec/rspec/mocks/argument_expectation_spec.rb +4 -4
- data/spec/rspec/mocks/block_return_value_spec.rb +49 -11
- data/spec/rspec/mocks/bug_report_10263_spec.rb +1 -1
- data/spec/rspec/mocks/bug_report_8165_spec.rb +2 -2
- data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +4 -4
- data/spec/rspec/mocks/double_spec.rb +7 -0
- data/spec/rspec/mocks/failing_argument_matchers_spec.rb +1 -0
- data/spec/rspec/mocks/matchers/receive_spec.rb +10 -8
- data/spec/rspec/mocks/mock_space_spec.rb +10 -0
- data/spec/rspec/mocks/mock_spec.rb +20 -1
- data/spec/rspec/mocks/mutate_const_spec.rb +25 -25
- data/spec/rspec/mocks/null_object_mock_spec.rb +7 -0
- data/spec/rspec/mocks/passing_argument_matchers_spec.rb +4 -2
- data/spec/rspec/mocks/record_messages_spec.rb +4 -4
- data/spec/rspec/mocks/space_spec.rb +1 -1
- data/spec/rspec/mocks/stub_chain_spec.rb +8 -12
- data/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +20 -0
- data/spec/rspec/mocks/test_double_spec.rb +0 -5
- data/spec/rspec/mocks_spec.rb +14 -0
- data/spec/spec_helper.rb +27 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OThjNzA2ZTdkZmU2MWE5OWVhMzIzMjI4NjZkNGNlY2QwZTY1ZDMyYQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OGY4OTQwMGY4NTZhMGEzMmVhY2NlODAzZTYxMWMyNWFmZjVjMWIwYg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Njc3ODhhZTYxMzMyNWU3MzIxM2MzMTdjNWQ0N2Q4MWNiNTIyZDYwNGIzZjI4
|
10
|
+
NzRmZjk1YjYzNDExMmZkYWNiM2E4NjQ0ZWVkOWM4MGU2NzNiMjNlYjY4ZWEw
|
11
|
+
Zjg5ZDNkOTY0NGNkOTUxZjc1NWM3MDcyM2Y1NThjNDgwNDI1YjQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MGIzMGMxYzg2OTE4NzY1NzA5ZTM4MDIxZmRhZDQ1NjM0OWE0YjIwOWQ3MTFj
|
14
|
+
YWFiYzQwNGRhMDEzNTc2YWVlNzYyMDcwNTNiZGU5MWE4NmZiMzc4MGYzNzQ5
|
15
|
+
NWM1YjA3OTBmMzg5Njk1MWZkZDFjNmUxNDc4ODA5Y2NiMTAyNGQ=
|
data/Changelog.md
CHANGED
@@ -1,20 +1,27 @@
|
|
1
|
-
### 2.
|
2
|
-
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.
|
1
|
+
### 2.99.0.beta1 / 2013-11-07
|
2
|
+
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.99.0.beta1)
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
* Ensure `any_instance` method stubs and expectations are torn down regardless of
|
7
|
-
expectation failures. (Sam Phippen)
|
4
|
+
Deprecations
|
8
5
|
|
9
|
-
|
10
|
-
|
6
|
+
* Expecting to use lambdas or other strong arity implementations for stub
|
7
|
+
methods with mis-matched arity is deprecated and support for them will be
|
8
|
+
removed in 3.0. Either provide the right amount of arguments or use a weak
|
9
|
+
arity implementation (methods with splats or procs). (Jon Rowe)
|
10
|
+
* Using the same test double instance in multiple examples is deprecated. Test
|
11
|
+
doubles are only meant to live for one example. The mocks and stubs have
|
12
|
+
always been reset between examples; however, in 2.x the `as_null_object`
|
13
|
+
state was not reset and some users relied on this to have a null object
|
14
|
+
double that is used for many examples. This behavior will be removed in 3.0.
|
15
|
+
(Myron Marston)
|
16
|
+
* Print a detailed warning when an `any_instance` implementation block is used
|
17
|
+
when the new `yield_receiver_to_any_instance_implementation_blocks` config
|
18
|
+
option is not explicitly set, as RSpec 3.0 will default to enabling this new
|
19
|
+
feature. (Sam Phippen)
|
11
20
|
|
12
|
-
|
21
|
+
Enhancements:
|
13
22
|
|
14
|
-
*
|
15
|
-
|
16
|
-
reporting `1` no matter how many args the block receives if it
|
17
|
-
receives a block, too. (Myron Marston)
|
23
|
+
* Add a config option to yield the receiver to `any_instance` implementation
|
24
|
+
blocks. (Sam Phippen)
|
18
25
|
|
19
26
|
### 2.14.4 / 2013-10-15
|
20
27
|
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.3...v2.14.4)
|
@@ -119,7 +126,6 @@ Bug fixes
|
|
119
126
|
(Luke Imhoff, Jon Rowe)
|
120
127
|
* Fix isolation of `allow_message_expectations_on_nil` (Jon Rowe)
|
121
128
|
* Use inspect to format actual arguments on expectations in failure messages (#280, Ben Langfeld)
|
122
|
-
* Protect against improperly initialised test doubles (#293) (Joseph Shraibman and Jon Rowe)
|
123
129
|
|
124
130
|
Deprecations
|
125
131
|
|
data/lib/rspec/mocks.rb
CHANGED
@@ -47,7 +47,9 @@ module RSpec
|
|
47
47
|
# x = 0
|
48
48
|
# RSpec::Mocks.allow_message(bar, :foo) { x += 1 }
|
49
49
|
def allow_message(subject, message, opts={}, &block)
|
50
|
-
orig_caller = opts.fetch(:expected_from) {
|
50
|
+
orig_caller = opts.fetch(:expected_from) {
|
51
|
+
CallerFilter.first_non_rspec_line
|
52
|
+
}
|
51
53
|
::RSpec::Mocks.proxy_for(subject).
|
52
54
|
add_stub(orig_caller, message.to_sym, opts, &block)
|
53
55
|
end
|
@@ -64,7 +66,9 @@ module RSpec
|
|
64
66
|
# RSpec::Mocks.expect_message(bar, :foo)
|
65
67
|
# bar.foo
|
66
68
|
def expect_message(subject, message, opts={}, &block)
|
67
|
-
orig_caller = opts.fetch(:expected_from) {
|
69
|
+
orig_caller = opts.fetch(:expected_from) {
|
70
|
+
CallerFilter.first_non_rspec_line
|
71
|
+
}
|
68
72
|
::RSpec::Mocks.proxy_for(subject).
|
69
73
|
add_message_expectation(orig_caller, message.to_sym, opts, &block)
|
70
74
|
end
|
@@ -6,6 +6,8 @@ module RSpec
|
|
6
6
|
@recorder = recorder
|
7
7
|
@expectation_args = args
|
8
8
|
@expectation_block = block
|
9
|
+
@source_line = CallerFilter.first_non_rspec_line
|
10
|
+
ensure_expectation_block_has_source_location
|
9
11
|
end
|
10
12
|
|
11
13
|
module Customizations
|
@@ -71,6 +73,19 @@ module RSpec
|
|
71
73
|
|
72
74
|
private
|
73
75
|
|
76
|
+
def create_message_expectation_on(instance)
|
77
|
+
me = yield(::RSpec::Mocks.proxy_for(instance), IGNORED_BACKTRACE_LINE)
|
78
|
+
|
79
|
+
if RSpec::Mocks.configuration.should_warn_about_any_instance_blocks?
|
80
|
+
me.warn_about_receiver_passing(@source_line)
|
81
|
+
me.display_any_instance_deprecation_warning_if_necessary(@expectation_block)
|
82
|
+
elsif RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks?
|
83
|
+
me.and_yield_receiver_to_implementation
|
84
|
+
end
|
85
|
+
|
86
|
+
me
|
87
|
+
end
|
88
|
+
|
74
89
|
def negated?
|
75
90
|
messages.any? { |(message, *_), _| message.to_sym == :never }
|
76
91
|
end
|
@@ -88,6 +103,19 @@ module RSpec
|
|
88
103
|
messages << [args.unshift(rspec_method_name), block]
|
89
104
|
self
|
90
105
|
end
|
106
|
+
|
107
|
+
if Proc.method_defined?(:source_location)
|
108
|
+
def ensure_expectation_block_has_source_location; end
|
109
|
+
else
|
110
|
+
def ensure_expectation_block_has_source_location
|
111
|
+
return unless @expectation_block
|
112
|
+
source_location = CallerFilter.first_non_rspec_line.split(':')
|
113
|
+
|
114
|
+
@expectation_block.extend Module.new {
|
115
|
+
define_method(:source_location) { source_location }
|
116
|
+
}
|
117
|
+
end
|
118
|
+
end
|
91
119
|
end
|
92
120
|
end
|
93
121
|
end
|
@@ -12,20 +12,15 @@ module RSpec
|
|
12
12
|
super
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
private
|
16
|
+
|
16
17
|
def verify_invocation_order(rspec_method_name, *args, &block)
|
17
18
|
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# @api private
|
21
|
-
class PositiveExpectationChain < ExpectationChain
|
22
|
-
|
23
|
-
private
|
24
19
|
|
25
20
|
def create_message_expectation_on(instance)
|
26
|
-
proxy
|
27
|
-
|
28
|
-
|
21
|
+
super do |proxy, expected_from|
|
22
|
+
proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block)
|
23
|
+
end
|
29
24
|
end
|
30
25
|
|
31
26
|
def invocation_order
|
@@ -57,7 +57,7 @@ module RSpec
|
|
57
57
|
def should_receive(method_name, &block)
|
58
58
|
@expectation_set = true
|
59
59
|
observe!(method_name)
|
60
|
-
message_chains.add(method_name,
|
60
|
+
message_chains.add(method_name, ExpectationChain.new(self, method_name, &block))
|
61
61
|
end
|
62
62
|
|
63
63
|
def should_not_receive(method_name, &block)
|
@@ -88,6 +88,9 @@ module RSpec
|
|
88
88
|
if @expectation_set && !message_chains.all_expectations_fulfilled?
|
89
89
|
raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}"
|
90
90
|
end
|
91
|
+
ensure
|
92
|
+
stop_all_observation!
|
93
|
+
::RSpec::Mocks.space.remove_any_instance_recorder_for(@klass)
|
91
94
|
end
|
92
95
|
|
93
96
|
# @private
|
@@ -12,11 +12,11 @@ module RSpec
|
|
12
12
|
private
|
13
13
|
|
14
14
|
def create_message_expectation_on(instance)
|
15
|
-
proxy
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
super do |proxy, expected_from|
|
16
|
+
stub = proxy.add_stub(expected_from, *@expectation_args, &@expectation_block)
|
17
|
+
@recorder.stubs[stub.message] << stub
|
18
|
+
stub
|
19
|
+
end
|
20
20
|
end
|
21
21
|
|
22
22
|
def invocation_order
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RSpec
|
2
|
+
# Consistent implementation for "cleaning" the caller method to strip out
|
3
|
+
# non-rspec lines. This enables errors to be reported at the call site in
|
4
|
+
# the code using the library, which is far more useful than the particular
|
5
|
+
# internal method that raised an error.
|
6
|
+
class CallerFilter
|
7
|
+
|
8
|
+
RSPEC_LIBS = %w[
|
9
|
+
core
|
10
|
+
mocks
|
11
|
+
expectations
|
12
|
+
matchers
|
13
|
+
rails
|
14
|
+
]
|
15
|
+
|
16
|
+
ADDITIONAL_TOP_LEVEL_FILES = %w[ autorun ]
|
17
|
+
|
18
|
+
LIB_REGEX = %r{/lib/rspec/(#{(RSPEC_LIBS + ADDITIONAL_TOP_LEVEL_FILES).join('|')})(\.rb|/)}
|
19
|
+
|
20
|
+
if RUBY_VERSION >= '2.0.0'
|
21
|
+
def self.first_non_rspec_line
|
22
|
+
# `caller` is an expensive method that scales linearly with the size of
|
23
|
+
# the stack. The performance hit for fetching it in chunks is small,
|
24
|
+
# and since the target line is probably near the top of the stack, the
|
25
|
+
# overall improvement of a chunked search like this is significant.
|
26
|
+
#
|
27
|
+
# See benchmarks/caller.rb for measurements.
|
28
|
+
|
29
|
+
# Initial value here is mostly arbitrary, but is chosen to give good
|
30
|
+
# performance on the common case of creating a double.
|
31
|
+
increment = 5
|
32
|
+
i = 1
|
33
|
+
line = nil
|
34
|
+
|
35
|
+
while !line
|
36
|
+
stack = caller(i, increment)
|
37
|
+
raise "No non-lib lines in stack" unless stack
|
38
|
+
|
39
|
+
line = stack.find { |l| l !~ LIB_REGEX }
|
40
|
+
|
41
|
+
i += increment
|
42
|
+
increment *= 2 # The choice of two here is arbitrary.
|
43
|
+
end
|
44
|
+
|
45
|
+
line
|
46
|
+
end
|
47
|
+
else
|
48
|
+
# Earlier rubies do not support the two argument form of `caller`. This
|
49
|
+
# fallback is logically the same, but slower.
|
50
|
+
def self.first_non_rspec_line
|
51
|
+
caller.find { |line| line !~ LIB_REGEX }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -2,6 +2,25 @@ module RSpec
|
|
2
2
|
module Mocks
|
3
3
|
# Provides configuration options for rspec-mocks.
|
4
4
|
class Configuration
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@yield_receiver_to_any_instance_implementation_blocks = false
|
8
|
+
@should_warn_about_any_instance_blocks = true
|
9
|
+
end
|
10
|
+
|
11
|
+
def yield_receiver_to_any_instance_implementation_blocks?
|
12
|
+
@yield_receiver_to_any_instance_implementation_blocks
|
13
|
+
end
|
14
|
+
|
15
|
+
def yield_receiver_to_any_instance_implementation_blocks=(arg)
|
16
|
+
@should_warn_about_any_instance_blocks = false
|
17
|
+
@yield_receiver_to_any_instance_implementation_blocks = arg
|
18
|
+
end
|
19
|
+
|
20
|
+
def should_warn_about_any_instance_blocks?
|
21
|
+
@should_warn_about_any_instance_blocks
|
22
|
+
end
|
23
|
+
|
5
24
|
# Adds `stub` and `should_receive` to the given
|
6
25
|
# modules or classes. This is usually only necessary
|
7
26
|
# if you application uses some proxy classes that
|
@@ -7,7 +7,15 @@ module RSpec
|
|
7
7
|
def deprecate(deprecated, options={})
|
8
8
|
message = "DEPRECATION: #{deprecated} is deprecated."
|
9
9
|
message << " Use #{options[:replacement]} instead." if options[:replacement]
|
10
|
-
message << " Called from #{
|
10
|
+
message << " Called from #{CallerFilter.first_non_rspec_line}."
|
11
|
+
warn message
|
12
|
+
end
|
13
|
+
|
14
|
+
# @private
|
15
|
+
#
|
16
|
+
# Used internally to print deprecation warnings
|
17
|
+
def warn_deprecation(warning)
|
18
|
+
message = "\nDEPRECATION: #{warning}\n"
|
11
19
|
warn message
|
12
20
|
end
|
13
21
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
unless Proc.method_defined? :lambda?
|
2
|
+
module Backports
|
3
|
+
def self.alias_method_chain(mod, target, feature)
|
4
|
+
mod.class_eval do
|
5
|
+
# Strip out punctuation on predicates or bang methods since
|
6
|
+
# e.g. target?_without_feature is not a valid method name.
|
7
|
+
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
|
8
|
+
yield(aliased_target, punctuation) if block_given?
|
9
|
+
|
10
|
+
with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
|
11
|
+
|
12
|
+
alias_method without_method, target
|
13
|
+
alias_method target, with_method
|
14
|
+
|
15
|
+
case
|
16
|
+
when public_method_defined?(without_method)
|
17
|
+
public target
|
18
|
+
when protected_method_defined?(without_method)
|
19
|
+
protected target
|
20
|
+
when private_method_defined?(without_method)
|
21
|
+
private target
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Proc
|
28
|
+
# Standard in Ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Proc.html]
|
29
|
+
def lambda?
|
30
|
+
!!__is_lambda__
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_accessor :__is_lambda__
|
34
|
+
private :__is_lambda__
|
35
|
+
private :__is_lambda__=
|
36
|
+
end
|
37
|
+
|
38
|
+
class Method
|
39
|
+
def to_proc_with_lambda_tracking
|
40
|
+
proc = to_proc_without_lambda_tracking
|
41
|
+
proc.send :__is_lambda__=, true
|
42
|
+
proc
|
43
|
+
end
|
44
|
+
Backports.alias_method_chain self, :to_proc, :lambda_tracking
|
45
|
+
end
|
46
|
+
|
47
|
+
module Kernel
|
48
|
+
def lambda_with_lambda_tracking(&block)
|
49
|
+
l = lambda_without_lambda_tracking(&block)
|
50
|
+
l.send :__is_lambda__=, true unless block.send(:__is_lambda__) == false
|
51
|
+
l
|
52
|
+
end
|
53
|
+
|
54
|
+
def proc_with_lambda_tracking(&block)
|
55
|
+
l = proc_without_lambda_tracking(&block)
|
56
|
+
l.send :__is_lambda__=, block.send(:__is_lambda__) == true
|
57
|
+
l
|
58
|
+
end
|
59
|
+
|
60
|
+
Backports.alias_method_chain self, :lambda, :lambda_tracking
|
61
|
+
Backports.alias_method_chain self, :proc, :lambda_tracking
|
62
|
+
end
|
63
|
+
end
|
@@ -2,8 +2,10 @@
|
|
2
2
|
# supports wrapping rspec's mocking functionality without invading every
|
3
3
|
# object in the system.
|
4
4
|
|
5
|
+
require 'rspec/mocks/caller_filter' unless defined?(::RSpec::CallerFilter)
|
5
6
|
require 'rspec/mocks/deprecation'
|
6
7
|
require 'rspec/mocks/extensions/instance_exec'
|
8
|
+
require 'rspec/mocks/extensions/proc'
|
7
9
|
require 'rspec/mocks/instance_method_stasher'
|
8
10
|
require 'rspec/mocks/method_double'
|
9
11
|
require 'rspec/mocks/argument_matchers'
|
@@ -6,14 +6,7 @@ module RSpec
|
|
6
6
|
@message = message
|
7
7
|
@block = block
|
8
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
|
9
|
+
@backtrace_line = CallerFilter.first_non_rspec_line
|
17
10
|
end
|
18
11
|
|
19
12
|
def setup_expectation(subject, &block)
|
@@ -49,7 +42,7 @@ module RSpec
|
|
49
42
|
MessageExpectation.public_instance_methods(false).each do |method|
|
50
43
|
next if method_defined?(method)
|
51
44
|
|
52
|
-
class_eval(<<-RUBY)
|
45
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
53
46
|
def #{method}(*args, &block)
|
54
47
|
@recorded_customizations << Customization.new(#{method.inspect}, args, block)
|
55
48
|
self
|
@@ -4,7 +4,9 @@ module RSpec
|
|
4
4
|
class MessageExpectation
|
5
5
|
# @private
|
6
6
|
attr_accessor :error_generator, :implementation
|
7
|
+
attr_accessor :warn_about_yielding_receiver_to_implementation_block
|
7
8
|
attr_reader :message
|
9
|
+
attr_reader :orig_object
|
8
10
|
attr_writer :expected_received_count, :expected_from, :argument_list_matcher
|
9
11
|
protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
|
10
12
|
|
@@ -15,6 +17,9 @@ module RSpec
|
|
15
17
|
@error_generator.opts = opts
|
16
18
|
@expected_from = expected_from
|
17
19
|
@method_double = method_double
|
20
|
+
@have_warned_about_yielding_receiver = false
|
21
|
+
@orig_object = @method_double.object
|
22
|
+
@warn_about_yielding_receiver_to_implementation_block = false
|
18
23
|
@message = @method_double.method_name
|
19
24
|
@actual_received_count = 0
|
20
25
|
@expected_received_count = expected_received_count
|
@@ -24,6 +29,7 @@ module RSpec
|
|
24
29
|
@args_to_yield = []
|
25
30
|
@failed_fast = nil
|
26
31
|
@eval_context = nil
|
32
|
+
@yield_receiver_to_implementation_block = false
|
27
33
|
|
28
34
|
@implementation = Implementation.new
|
29
35
|
self.inner_implementation_action = implementation_block
|
@@ -87,6 +93,15 @@ module RSpec
|
|
87
93
|
nil
|
88
94
|
end
|
89
95
|
|
96
|
+
def and_yield_receiver_to_implementation
|
97
|
+
@yield_receiver_to_implementation_block = true
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
def yield_receiver_to_implementation_block?
|
102
|
+
@yield_receiver_to_implementation_block
|
103
|
+
end
|
104
|
+
|
90
105
|
# Tells the object to delegate to the original unmodified method
|
91
106
|
# when it receives the message.
|
92
107
|
#
|
@@ -103,6 +118,7 @@ module RSpec
|
|
103
118
|
@error_generator.raise_only_valid_on_a_partial_mock(:and_call_original)
|
104
119
|
else
|
105
120
|
@implementation = AndCallOriginalImplementation.new(@method_double.original_method)
|
121
|
+
@yield_receiver_to_implementation_block = false
|
106
122
|
end
|
107
123
|
end
|
108
124
|
|
@@ -171,6 +187,10 @@ module RSpec
|
|
171
187
|
|
172
188
|
# @private
|
173
189
|
def invoke(parent_stub, *args, &block)
|
190
|
+
if yield_receiver_to_implementation_block?
|
191
|
+
args.unshift(orig_object)
|
192
|
+
end
|
193
|
+
|
174
194
|
if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
|
175
195
|
@actual_received_count += 1
|
176
196
|
@failed_fast = true
|
@@ -218,6 +238,28 @@ module RSpec
|
|
218
238
|
Kernel::raise error
|
219
239
|
end
|
220
240
|
|
241
|
+
# @private
|
242
|
+
def display_any_instance_deprecation_warning_if_necessary(block)
|
243
|
+
if passing_an_additional_arg_would_break_block?(block) &&
|
244
|
+
should_display_any_instance_deprecation_warning
|
245
|
+
line = if block.respond_to?(:source_location)
|
246
|
+
block.source_location.join(':')
|
247
|
+
else
|
248
|
+
@any_instance_source_line
|
249
|
+
end
|
250
|
+
|
251
|
+
display_any_instance_deprecation_warning(line)
|
252
|
+
@have_warned_about_yielding_receiver = true
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# @private
|
257
|
+
def passing_an_additional_arg_would_break_block?(block)
|
258
|
+
return false unless block
|
259
|
+
return true if block.lambda?
|
260
|
+
!block.arity.zero?
|
261
|
+
end
|
262
|
+
|
221
263
|
# @private
|
222
264
|
def expected_messages_received?
|
223
265
|
ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
|
@@ -302,7 +344,15 @@ module RSpec
|
|
302
344
|
# cart.add(Book.new(:isbn => 1934356379))
|
303
345
|
# # => passes
|
304
346
|
def with(*args, &block)
|
305
|
-
|
347
|
+
if block_given?
|
348
|
+
if args.empty?
|
349
|
+
RSpec.deprecate "Using the return value of a `with` block to validate passed arguments rather than as an implementation",
|
350
|
+
:replacement => "the `satisfy` matcher, a custom matcher or validate the arguments in an implementation block"
|
351
|
+
else
|
352
|
+
self.inner_implementation_action = block
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
306
356
|
@argument_list_matcher = ArgumentListMatcher.new(*args, &block)
|
307
357
|
self
|
308
358
|
end
|
@@ -430,6 +480,38 @@ module RSpec
|
|
430
480
|
@actual_received_count += 1
|
431
481
|
end
|
432
482
|
|
483
|
+
def warn_about_receiver_passing(any_instance_source_line)
|
484
|
+
@any_instance_source_line = any_instance_source_line
|
485
|
+
@warn_about_yielding_receiver_to_implementation_block = true
|
486
|
+
end
|
487
|
+
|
488
|
+
def should_display_any_instance_deprecation_warning
|
489
|
+
warn_about_yielding_receiver_to_implementation_block &&
|
490
|
+
!@have_warned_about_yielding_receiver
|
491
|
+
end
|
492
|
+
|
493
|
+
def display_any_instance_deprecation_warning(block_source_line)
|
494
|
+
RSpec.warn_deprecation(<<MSG
|
495
|
+
In RSpec 3, `any_instance` implementation blocks will be yielded the receiving
|
496
|
+
instance as the first block argument to allow the implementation block to use
|
497
|
+
the state of the receiver. To maintain compatibility with RSpec 3 you need to
|
498
|
+
either set rspec-mocks' `yield_receiver_to_any_instance_implementation_blocks`
|
499
|
+
config option to `false` OR set it to `true` and update your `any_instance`
|
500
|
+
implementation blocks to account for the first block argument being the receiving instance.
|
501
|
+
|
502
|
+
To set the config option, use a snippet like:
|
503
|
+
|
504
|
+
RSpec.configure do |rspec|
|
505
|
+
rspec.mock_with :rspec do |mocks|
|
506
|
+
mocks.yield_receiver_to_any_instance_implementation_blocks = false
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
Your `any_instance` implementation block is declared at: #{block_source_line}
|
511
|
+
MSG
|
512
|
+
)
|
513
|
+
end
|
514
|
+
|
433
515
|
private
|
434
516
|
|
435
517
|
def failed_fast?
|
@@ -452,6 +534,7 @@ module RSpec
|
|
452
534
|
end
|
453
535
|
|
454
536
|
def inner_implementation_action=(action)
|
537
|
+
display_any_instance_deprecation_warning_if_necessary(action)
|
455
538
|
implementation.inner_action = action if action
|
456
539
|
end
|
457
540
|
|
@@ -516,31 +599,18 @@ module RSpec
|
|
516
599
|
|
517
600
|
def call(*args, &block)
|
518
601
|
actions.map do |action|
|
602
|
+
if action.respond_to?(:lambda?) && action.lambda? && action.arity != args.size
|
603
|
+
RSpec.deprecate "stubbing implementations with mismatched arity",
|
604
|
+
:call_site => CallerFilter.first_non_rspec_line
|
605
|
+
end
|
519
606
|
action.call(*arg_slice_for(args, action.arity), &block)
|
520
607
|
end.last
|
521
608
|
end
|
522
609
|
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
else
|
528
|
-
args
|
529
|
-
end
|
530
|
-
end
|
531
|
-
else
|
532
|
-
# 1.8.7's `arity` lies somtimes:
|
533
|
-
# Given:
|
534
|
-
# def print_arity(&b) puts b.arity; end
|
535
|
-
#
|
536
|
-
# This prints 1:
|
537
|
-
# print_arity { |a, b, c, &bl| }
|
538
|
-
#
|
539
|
-
# But this prints 3:
|
540
|
-
# print_arity { |a, b, c| }
|
541
|
-
#
|
542
|
-
# Given that it lies, we can't trust it and we don't slice the args.
|
543
|
-
def arg_slice_for(args, arity)
|
610
|
+
def arg_slice_for(args, arity)
|
611
|
+
if arity >= 0
|
612
|
+
args.slice(0, arity)
|
613
|
+
else
|
544
614
|
args
|
545
615
|
end
|
546
616
|
end
|