rspec-mocks 2.99.4 → 3.0.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 +14 -6
- checksums.yaml.gz.sig +2 -0
- data.tar.gz.sig +1 -0
- data/Changelog.md +89 -105
- data/License.txt +1 -0
- data/README.md +77 -57
- data/features/argument_matchers/explicit.feature +5 -5
- data/features/argument_matchers/general_matchers.feature +10 -10
- data/features/argument_matchers/type_matchers.feature +3 -3
- data/features/message_expectations/allow_any_instance_of.feature +1 -1
- data/features/message_expectations/any_instance.feature +27 -5
- data/features/message_expectations/call_original.feature +2 -2
- data/features/message_expectations/expect_message_using_expect.feature +2 -2
- data/features/message_expectations/expect_message_using_should_receive.feature +2 -2
- data/features/message_expectations/receive_counts.feature +7 -7
- data/features/message_expectations/warn_when_expectation_is_set_on_nil.feature +3 -3
- data/features/method_stubs/README.md +3 -0
- data/features/method_stubs/any_instance.feature +11 -11
- data/features/method_stubs/as_null_object.feature +4 -4
- data/features/method_stubs/simple_return_value_with_stub.feature +7 -7
- data/features/method_stubs/stub_chain.feature +3 -3
- data/features/method_stubs/stub_implementation.feature +2 -2
- data/features/method_stubs/to_ary.feature +2 -2
- data/features/mutating_constants/hiding_defined_constant.feature +2 -2
- data/features/mutating_constants/stub_defined_constant.feature +5 -5
- data/features/mutating_constants/stub_undefined_constant.feature +6 -6
- data/features/outside_rspec/configuration.feature +0 -2
- data/features/outside_rspec/standalone.feature +1 -1
- data/features/spies/spy_partial_mock_method.feature +2 -2
- data/features/spies/spy_pure_mock_method.feature +5 -5
- data/features/spies/spy_unstubbed_method.feature +1 -1
- data/features/support/env.rb +10 -1
- data/features/test_frameworks/test_unit.feature +1 -1
- data/features/verifying_doubles/class_doubles.feature +88 -0
- data/features/verifying_doubles/dynamic_classes.feature +72 -0
- data/features/verifying_doubles/introduction.feature +85 -0
- data/features/verifying_doubles/object_doubles.feature +65 -0
- data/features/verifying_doubles/partial_doubles.feature +34 -0
- data/lib/rspec/mocks.rb +8 -34
- data/lib/rspec/mocks/any_instance/chain.rb +4 -34
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +14 -4
- data/lib/rspec/mocks/any_instance/message_chains.rb +27 -12
- data/lib/rspec/mocks/any_instance/recorder.rb +23 -31
- data/lib/rspec/mocks/any_instance/stub_chain.rb +9 -4
- data/lib/rspec/mocks/argument_list_matcher.rb +8 -1
- data/lib/rspec/mocks/argument_matchers.rb +26 -12
- data/lib/rspec/mocks/arity_calculator.rb +66 -0
- data/lib/rspec/mocks/configuration.rb +42 -14
- data/lib/rspec/mocks/error_generator.rb +34 -10
- data/lib/rspec/mocks/example_methods.rb +64 -19
- data/lib/rspec/mocks/extensions/marshal.rb +0 -15
- data/lib/rspec/mocks/framework.rb +4 -4
- data/lib/rspec/mocks/instance_method_stasher.rb +80 -62
- data/lib/rspec/mocks/matchers/have_received.rb +18 -14
- data/lib/rspec/mocks/matchers/receive.rb +29 -7
- data/lib/rspec/mocks/matchers/receive_messages.rb +72 -0
- data/lib/rspec/mocks/message_expectation.rb +95 -148
- data/lib/rspec/mocks/method_double.rb +77 -139
- data/lib/rspec/mocks/method_reference.rb +95 -0
- data/lib/rspec/mocks/mock.rb +1 -1
- data/lib/rspec/mocks/mutate_const.rb +12 -9
- data/lib/rspec/mocks/object_reference.rb +90 -0
- data/lib/rspec/mocks/order_group.rb +49 -7
- data/lib/rspec/mocks/proxy.rb +72 -33
- data/lib/rspec/mocks/proxy_for_nil.rb +2 -2
- data/lib/rspec/mocks/space.rb +13 -18
- data/lib/rspec/mocks/stub_chain.rb +2 -2
- data/lib/rspec/mocks/syntax.rb +61 -36
- data/lib/rspec/mocks/targets.rb +40 -19
- data/lib/rspec/mocks/test_double.rb +12 -56
- data/lib/rspec/mocks/verifying_double.rb +77 -0
- data/lib/rspec/mocks/verifying_message_expecation.rb +60 -0
- data/lib/rspec/mocks/verifying_proxy.rb +151 -0
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/and_call_original_spec.rb +34 -30
- data/spec/rspec/mocks/and_yield_spec.rb +2 -2
- data/spec/rspec/mocks/any_instance/message_chains_spec.rb +1 -1
- data/spec/rspec/mocks/any_instance_spec.rb +53 -260
- data/spec/rspec/mocks/argument_expectation_spec.rb +4 -4
- data/spec/rspec/mocks/arity_calculator_spec.rb +95 -0
- data/spec/rspec/mocks/array_including_matcher_spec.rb +41 -0
- data/spec/rspec/mocks/at_least_spec.rb +4 -32
- data/spec/rspec/mocks/block_return_value_spec.rb +4 -135
- data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +10 -11
- data/spec/rspec/mocks/configuration_spec.rb +79 -0
- data/spec/rspec/mocks/double_spec.rb +10 -78
- data/spec/rspec/mocks/extensions/marshal_spec.rb +0 -8
- data/spec/rspec/mocks/failing_argument_matchers_spec.rb +49 -4
- data/spec/rspec/mocks/instance_method_stasher_spec.rb +20 -3
- data/spec/rspec/mocks/matchers/have_received_spec.rb +74 -0
- data/spec/rspec/mocks/matchers/receive_messages_spec.rb +140 -0
- data/spec/rspec/mocks/matchers/receive_spec.rb +82 -42
- data/spec/rspec/mocks/methods_spec.rb +1 -1
- data/spec/rspec/mocks/{bug_report_830_spec.rb → mock_expectation_error_spec.rb} +4 -3
- data/spec/rspec/mocks/mock_ordering_spec.rb +11 -0
- data/spec/rspec/mocks/mock_space_spec.rb +10 -1
- data/spec/rspec/mocks/mock_spec.rb +26 -82
- data/spec/rspec/mocks/multiple_return_value_spec.rb +1 -1
- data/spec/rspec/mocks/mutate_const_spec.rb +18 -5
- data/spec/rspec/mocks/null_object_mock_spec.rb +6 -4
- data/spec/rspec/mocks/options_hash_spec.rb +3 -3
- data/spec/rspec/mocks/order_group_spec.rb +27 -0
- data/spec/rspec/mocks/partial_mock_spec.rb +101 -1
- data/spec/rspec/mocks/passing_argument_matchers_spec.rb +3 -20
- data/spec/rspec/mocks/record_messages_spec.rb +4 -4
- data/spec/rspec/mocks/serialization_spec.rb +4 -6
- data/spec/rspec/mocks/space_spec.rb +3 -3
- data/spec/rspec/mocks/stub_chain_spec.rb +0 -12
- data/spec/rspec/mocks/stub_spec.rb +23 -44
- data/spec/rspec/mocks/test_double_spec.rb +3 -22
- data/spec/rspec/mocks/verifying_double_spec.rb +327 -0
- data/spec/rspec/mocks/verifying_message_expecation_spec.rb +68 -0
- data/spec/rspec/mocks_spec.rb +16 -39
- data/spec/spec_helper.rb +29 -18
- metadata +131 -86
- metadata.gz.sig +1 -0
- data/features/message_expectations/expect_any_instance_of.feature +0 -27
- data/lib/rspec/mocks/caller_filter.rb +0 -60
- data/lib/rspec/mocks/deprecation.rb +0 -26
- data/lib/rspec/mocks/extensions/instance_exec.rb +0 -34
- data/lib/rspec/mocks/extensions/proc.rb +0 -63
- data/lib/spec/mocks.rb +0 -4
- data/spec/rspec/mocks/and_return_spec.rb +0 -17
- data/spec/rspec/mocks/any_number_of_times_spec.rb +0 -36
- data/spec/rspec/mocks/before_all_spec.rb +0 -74
- data/spec/rspec/mocks/bug_report_10260_spec.rb +0 -8
- data/spec/rspec/mocks/bug_report_10263_spec.rb +0 -27
- data/spec/rspec/mocks/bug_report_11545_spec.rb +0 -32
- data/spec/rspec/mocks/bug_report_496_spec.rb +0 -17
- data/spec/rspec/mocks/bug_report_600_spec.rb +0 -22
- data/spec/rspec/mocks/bug_report_7611_spec.rb +0 -16
- data/spec/rspec/mocks/bug_report_8165_spec.rb +0 -31
- data/spec/rspec/mocks/bug_report_957_spec.rb +0 -22
@@ -6,21 +6,6 @@ module Marshal
|
|
6
6
|
if ::RSpec::Mocks.space.nil? || !::RSpec::Mocks.space.registered?(object) || NilClass === object
|
7
7
|
dump_without_mocks(object, *rest)
|
8
8
|
else
|
9
|
-
unless ::RSpec::Mocks.configuration.marshal_patched?
|
10
|
-
RSpec.warn_deprecation(<<-EOS.gsub(/^\s+\|/, ''))
|
11
|
-
|Using Marshal.dump on stubbed objects relies on a monkey-patch
|
12
|
-
|that is being made opt-in in RSpec 3. To silence this warning
|
13
|
-
|please explicitly enable it:
|
14
|
-
|
|
15
|
-
|RSpec.configure do |rspec|
|
16
|
-
| rspec.mock_with :rspec do |mocks|
|
17
|
-
| mocks.patch_marshal_to_support_partial_doubles = true
|
18
|
-
| end
|
19
|
-
|end
|
20
|
-
|
|
21
|
-
|Called from #{RSpec::CallerFilter.first_non_rspec_line}."
|
22
|
-
EOS
|
23
|
-
end
|
24
9
|
dump_without_mocks(object.dup, *rest)
|
25
10
|
end
|
26
11
|
end
|
@@ -2,10 +2,8 @@
|
|
2
2
|
# supports wrapping rspec's mocking functionality without invading every
|
3
3
|
# object in the system.
|
4
4
|
|
5
|
-
require 'rspec/
|
6
|
-
require 'rspec/
|
7
|
-
require 'rspec/mocks/extensions/instance_exec'
|
8
|
-
require 'rspec/mocks/extensions/proc'
|
5
|
+
require 'rspec/support/caller_filter'
|
6
|
+
require 'rspec/support/warnings'
|
9
7
|
require 'rspec/mocks/instance_method_stasher'
|
10
8
|
require 'rspec/mocks/method_double'
|
11
9
|
require 'rspec/mocks/argument_matchers'
|
@@ -30,7 +28,9 @@ require 'rspec/mocks/any_instance/recorder'
|
|
30
28
|
require 'rspec/mocks/mutate_const'
|
31
29
|
require 'rspec/mocks/matchers/have_received'
|
32
30
|
require 'rspec/mocks/matchers/receive'
|
31
|
+
require 'rspec/mocks/matchers/receive_messages'
|
33
32
|
require 'rspec/mocks/stub_chain'
|
34
33
|
require 'rspec/mocks/targets'
|
35
34
|
require 'rspec/mocks/syntax'
|
36
35
|
require 'rspec/mocks/configuration'
|
36
|
+
require 'rspec/mocks/verifying_double'
|
@@ -2,91 +2,109 @@ module RSpec
|
|
2
2
|
module Mocks
|
3
3
|
# @private
|
4
4
|
class InstanceMethodStasher
|
5
|
-
def initialize(
|
6
|
-
@
|
5
|
+
def initialize(object, method)
|
6
|
+
@object = object
|
7
7
|
@method = method
|
8
|
+
@klass = (class << object; self; end)
|
8
9
|
|
10
|
+
@original_method = nil
|
9
11
|
@method_is_stashed = false
|
10
12
|
end
|
11
13
|
|
12
|
-
|
13
|
-
def method_is_stashed?
|
14
|
-
@method_is_stashed
|
15
|
-
end
|
14
|
+
attr_reader :original_method
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@method_is_stashed = true
|
23
|
-
end
|
16
|
+
if RUBY_VERSION.to_f < 1.9
|
17
|
+
# @private
|
18
|
+
def method_is_stashed?
|
19
|
+
@method_is_stashed
|
20
|
+
end
|
24
21
|
|
25
|
-
|
22
|
+
# @private
|
23
|
+
def stash
|
24
|
+
return if !method_defined_directly_on_klass? || @method_is_stashed
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
26
|
+
@klass.__send__(:alias_method, stashed_method_name, @method)
|
27
|
+
@method_is_stashed = true
|
28
|
+
end
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
# @private
|
31
|
+
def stashed_method_name
|
32
|
+
"obfuscated_by_rspec_mocks__#{@method}"
|
33
|
+
end
|
34
|
+
private :stashed_method_name
|
36
35
|
|
37
|
-
if ::UnboundMethod.method_defined?(:owner)
|
38
36
|
# @private
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
# can report the wrong owner. Example:
|
49
|
-
# class MyClass
|
50
|
-
# class << self
|
51
|
-
# alias alternate_new new
|
52
|
-
# end
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# MyClass.owner(:alternate_new) returns `Class` on 1.8,
|
56
|
-
# but we need to consider the owner to be `MyClass` because
|
57
|
-
# it is not actually available on `Class` but is on `MyClass`.
|
58
|
-
# Hence, we verify that the owner actually has the method defined.
|
59
|
-
# If the given owner does not have the method defined, we assume
|
60
|
-
# that the method is actually owned by @klass.
|
61
|
-
owner == @klass || !(method_defined_on_klass?(owner))
|
37
|
+
def restore
|
38
|
+
return unless @method_is_stashed
|
39
|
+
|
40
|
+
if @klass.__send__(:method_defined?, @method)
|
41
|
+
@klass.__send__(:undef_method, @method)
|
42
|
+
end
|
43
|
+
@klass.__send__(:alias_method, @method, stashed_method_name)
|
44
|
+
@klass.__send__(:remove_method, stashed_method_name)
|
45
|
+
@method_is_stashed = false
|
62
46
|
end
|
63
47
|
else
|
48
|
+
|
49
|
+
# @private
|
50
|
+
def method_is_stashed?
|
51
|
+
!!@original_method
|
52
|
+
end
|
53
|
+
|
64
54
|
# @private
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
55
|
+
def stash
|
56
|
+
return if !method_defined_directly_on_klass?
|
57
|
+
@original_method ||= ::RSpec::Mocks.method_handle_for(@object, @method)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @private
|
61
|
+
def restore
|
62
|
+
return unless @original_method
|
63
|
+
|
64
|
+
if @klass.__send__(:method_defined?, @method)
|
65
|
+
@klass.__send__(:undef_method, @method)
|
66
|
+
end
|
67
|
+
|
68
|
+
@klass.__send__(:define_method, @method, @original_method)
|
69
|
+
@original_method = nil
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
|
73
|
+
private
|
74
74
|
|
75
75
|
# @private
|
76
|
-
def
|
77
|
-
|
76
|
+
def method_defined_directly_on_klass?
|
77
|
+
method_defined_on_klass? && method_owned_by_klass?
|
78
78
|
end
|
79
79
|
|
80
80
|
# @private
|
81
|
-
def
|
82
|
-
|
81
|
+
def method_defined_on_klass?(klass = @klass)
|
82
|
+
klass.method_defined?(@method) || klass.private_method_defined?(@method)
|
83
|
+
end
|
83
84
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
85
|
+
def method_owned_by_klass?
|
86
|
+
owner = @klass.instance_method(@method).owner
|
87
|
+
|
88
|
+
# On Ruby 2.0.0+ the owner of a method on a class which has been
|
89
|
+
# `prepend`ed may actually be an instance, e.g.
|
90
|
+
# `#<MyClass:0x007fbb94e3cd10>`, rather than the expected `MyClass`.
|
91
|
+
owner = owner.class unless Module === owner
|
92
|
+
|
93
|
+
# On some 1.9s (e.g. rubinius) aliased methods
|
94
|
+
# can report the wrong owner. Example:
|
95
|
+
# class MyClass
|
96
|
+
# class << self
|
97
|
+
# alias alternate_new new
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# MyClass.owner(:alternate_new) returns `Class` when incorrect,
|
102
|
+
# but we need to consider the owner to be `MyClass` because
|
103
|
+
# it is not actually available on `Class` but is on `MyClass`.
|
104
|
+
# Hence, we verify that the owner actually has the method defined.
|
105
|
+
# If the given owner does not have the method defined, we assume
|
106
|
+
# that the method is actually owned by @klass.
|
107
|
+
owner == @klass || !(method_defined_on_klass?(owner))
|
90
108
|
end
|
91
109
|
end
|
92
110
|
end
|
@@ -4,25 +4,31 @@ module RSpec
|
|
4
4
|
class HaveReceived
|
5
5
|
COUNT_CONSTRAINTS = %w(exactly at_least at_most times once twice)
|
6
6
|
ARGS_CONSTRAINTS = %w(with)
|
7
|
-
CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS
|
7
|
+
CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w(ordered)
|
8
8
|
|
9
|
-
def initialize(method_name)
|
9
|
+
def initialize(method_name, &block)
|
10
10
|
@method_name = method_name
|
11
|
+
@block = block
|
11
12
|
@constraints = []
|
12
13
|
@subject = nil
|
13
14
|
end
|
14
15
|
|
15
|
-
def
|
16
|
+
def name
|
17
|
+
"have_received"
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(subject, &block)
|
21
|
+
@block ||= block
|
16
22
|
@subject = subject
|
17
23
|
@expectation = expect
|
18
|
-
|
24
|
+
expected_messages_received_in_order?
|
19
25
|
end
|
20
26
|
|
21
27
|
def does_not_match?(subject)
|
22
28
|
@subject = subject
|
23
29
|
ensure_count_unconstrained
|
24
30
|
@expectation = expect.never
|
25
|
-
|
31
|
+
expected_messages_received_in_order?
|
26
32
|
end
|
27
33
|
|
28
34
|
def failure_message
|
@@ -44,14 +50,12 @@ module RSpec
|
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
47
|
-
|
53
|
+
private
|
48
54
|
|
49
55
|
def expect
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
expectation
|
54
|
-
end
|
56
|
+
expectation = mock_proxy.build_expectation(@method_name)
|
57
|
+
apply_constraints_to expectation
|
58
|
+
expectation
|
55
59
|
end
|
56
60
|
|
57
61
|
def apply_constraints_to(expectation)
|
@@ -80,9 +84,9 @@ module RSpec
|
|
80
84
|
error.message
|
81
85
|
end
|
82
86
|
|
83
|
-
def
|
84
|
-
mock_proxy.replay_received_message_on @expectation
|
85
|
-
@expectation.expected_messages_received?
|
87
|
+
def expected_messages_received_in_order?
|
88
|
+
mock_proxy.replay_received_message_on @expectation, &@block
|
89
|
+
@expectation.expected_messages_received? && @expectation.ensure_expected_ordering_received!
|
86
90
|
end
|
87
91
|
|
88
92
|
def mock_proxy
|
@@ -6,10 +6,20 @@ module RSpec
|
|
6
6
|
@message = message
|
7
7
|
@block = block
|
8
8
|
@recorded_customizations = []
|
9
|
-
|
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 = CallerFilter.first_non_rspec_line
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
"receive"
|
10
19
|
end
|
11
20
|
|
12
21
|
def setup_expectation(subject, &block)
|
22
|
+
warn_if_any_instance("expect", subject)
|
13
23
|
setup_mock_proxy_method_substitute(subject, :add_message_expectation, block)
|
14
24
|
end
|
15
25
|
alias matches? setup_expectation
|
@@ -19,11 +29,14 @@ module RSpec
|
|
19
29
|
# where `and_return` is meant to raise an error
|
20
30
|
@recorded_customizations.unshift Customization.new(:never, [], nil)
|
21
31
|
|
32
|
+
warn_if_any_instance("expect", subject)
|
33
|
+
|
22
34
|
setup_expectation(subject, &block)
|
23
35
|
end
|
24
36
|
alias does_not_match? setup_negative_expectation
|
25
37
|
|
26
38
|
def setup_allowance(subject, &block)
|
39
|
+
warn_if_any_instance("allow", subject)
|
27
40
|
setup_mock_proxy_method_substitute(subject, :add_stub, block)
|
28
41
|
end
|
29
42
|
|
@@ -42,16 +55,25 @@ module RSpec
|
|
42
55
|
MessageExpectation.public_instance_methods(false).each do |method|
|
43
56
|
next if method_defined?(method)
|
44
57
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
50
|
-
RUBY
|
58
|
+
define_method(method) do |*args, &block|
|
59
|
+
@recorded_customizations << Customization.new(method, args, block)
|
60
|
+
self
|
61
|
+
end
|
51
62
|
end
|
52
63
|
|
53
64
|
private
|
54
65
|
|
66
|
+
def warn_if_any_instance(expression, subject)
|
67
|
+
if AnyInstance::Recorder === subject
|
68
|
+
RSpec.warning(
|
69
|
+
"`#{expression}(#{subject.klass}.any_instance).to` " <<
|
70
|
+
"is probably not what you meant, it does not operate on " <<
|
71
|
+
"any instance of `#{subject.klass}`. " <<
|
72
|
+
"Use `#{expression}_any_instance_of(#{subject.klass}).to` instead."
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
55
77
|
def setup_mock_proxy_method_substitute(subject, method, block)
|
56
78
|
proxy = ::RSpec::Mocks.proxy_for(subject)
|
57
79
|
setup_method_substitute(proxy, method, block, @backtrace_line)
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Mocks
|
3
|
+
module Matchers
|
4
|
+
class ReceiveMessages
|
5
|
+
|
6
|
+
def initialize(message_return_value_hash)
|
7
|
+
@message_return_value_hash = message_return_value_hash
|
8
|
+
@backtrace_line = CallerFilter.first_non_rspec_line
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
"receive_messages"
|
13
|
+
end
|
14
|
+
|
15
|
+
def setup_expectation(subject)
|
16
|
+
warn_about_block if block_given?
|
17
|
+
each_message_on( proxy_on(subject) ) do |host, message, return_value|
|
18
|
+
host.add_simple_expectation(message, return_value, @backtrace_line)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
alias matches? setup_expectation
|
22
|
+
|
23
|
+
def setup_negative_expectation(subject)
|
24
|
+
raise NegationUnsupportedError,
|
25
|
+
"`expect(...).to_not receive_messages` is not supported since it " +
|
26
|
+
"doesn't really make sense. What would it even mean?"
|
27
|
+
end
|
28
|
+
alias does_not_match? setup_negative_expectation
|
29
|
+
|
30
|
+
def setup_allowance(subject)
|
31
|
+
warn_about_block if block_given?
|
32
|
+
each_message_on( proxy_on(subject) ) do |host, message, return_value|
|
33
|
+
host.add_simple_stub(message, return_value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def setup_any_instance_expectation(subject)
|
38
|
+
warn_about_block if block_given?
|
39
|
+
each_message_on( any_instance_of(subject) ) do |host, message, return_value|
|
40
|
+
host.should_receive(message).and_return(return_value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def setup_any_instance_allowance(subject)
|
45
|
+
warn_about_block if block_given?
|
46
|
+
any_instance_of(subject).stub(@message_return_value_hash)
|
47
|
+
end
|
48
|
+
|
49
|
+
def warn_about_block
|
50
|
+
raise "Implementation blocks aren't supported with `receive_messages`"
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def proxy_on(subject)
|
56
|
+
::RSpec::Mocks.proxy_for(subject)
|
57
|
+
end
|
58
|
+
|
59
|
+
def any_instance_of(subject)
|
60
|
+
::RSpec::Mocks.any_instance_recorder_for(subject)
|
61
|
+
end
|
62
|
+
|
63
|
+
def each_message_on(host)
|
64
|
+
@message_return_value_hash.each do |message, value|
|
65
|
+
yield host, message, value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -1,10 +1,42 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Mocks
|
3
3
|
|
4
|
+
# A message expectation that only allows concrete return values to be set
|
5
|
+
# for a message. While this same effect can be achieved using a standard
|
6
|
+
# MessageExpecation, this version is much faster and so can be used as an
|
7
|
+
# optimization.
|
8
|
+
class SimpleMessageExpectation
|
9
|
+
|
10
|
+
def initialize(message, response, error_generator, backtrace_line = nil)
|
11
|
+
@message, @response, @error_generator, @backtrace_line = message, response, error_generator, backtrace_line
|
12
|
+
@received = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def invoke(*_)
|
16
|
+
@received = true
|
17
|
+
@response
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(message, *_)
|
21
|
+
@message == message
|
22
|
+
end
|
23
|
+
|
24
|
+
def called_max_times?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def verify_messages_received
|
29
|
+
InsertOntoBacktrace.line(@backtrace_line) do
|
30
|
+
unless @received
|
31
|
+
@error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
4
37
|
class MessageExpectation
|
5
38
|
# @private
|
6
39
|
attr_accessor :error_generator, :implementation
|
7
|
-
attr_accessor :warn_about_yielding_receiver_to_implementation_block
|
8
40
|
attr_reader :message
|
9
41
|
attr_reader :orig_object
|
10
42
|
attr_writer :expected_received_count, :expected_from, :argument_list_matcher
|
@@ -17,14 +49,14 @@ module RSpec
|
|
17
49
|
@error_generator.opts = opts
|
18
50
|
@expected_from = expected_from
|
19
51
|
@method_double = method_double
|
20
|
-
@have_warned_about_yielding_receiver = false
|
21
52
|
@orig_object = @method_double.object
|
22
|
-
@warn_about_yielding_receiver_to_implementation_block = false
|
23
53
|
@message = @method_double.method_name
|
24
54
|
@actual_received_count = 0
|
25
55
|
@expected_received_count = expected_received_count
|
26
|
-
@argument_list_matcher = ArgumentListMatcher
|
56
|
+
@argument_list_matcher = ArgumentListMatcher::MATCH_ALL
|
27
57
|
@order_group = expectation_ordering
|
58
|
+
@order_group.register(self)
|
59
|
+
@ordered = false
|
28
60
|
@at_least = @at_most = @exactly = nil
|
29
61
|
@args_to_yield = []
|
30
62
|
@failed_fast = nil
|
@@ -35,6 +67,8 @@ module RSpec
|
|
35
67
|
self.inner_implementation_action = implementation_block
|
36
68
|
end
|
37
69
|
|
70
|
+
# @private
|
71
|
+
|
38
72
|
# @private
|
39
73
|
def expected_args
|
40
74
|
@argument_list_matcher.expected_args
|
@@ -52,8 +86,8 @@ module RSpec
|
|
52
86
|
# If the message is received more times than there are values, the last
|
53
87
|
# value is received for every subsequent call.
|
54
88
|
#
|
55
|
-
# The block format is
|
56
|
-
# stub method.
|
89
|
+
# The block format is still supported, but is unofficially deprecated in
|
90
|
+
# favor of just passing a block to the stub method.
|
57
91
|
#
|
58
92
|
# @example
|
59
93
|
#
|
@@ -69,35 +103,28 @@ module RSpec
|
|
69
103
|
# counter.count # => 3
|
70
104
|
# # etc
|
71
105
|
#
|
72
|
-
# #
|
106
|
+
# # Supported, but ...
|
73
107
|
# counter.stub(:count).and_return { 1 }
|
74
108
|
# counter.count # => 1
|
75
109
|
#
|
76
|
-
# # ...
|
110
|
+
# # ... this is prefered
|
77
111
|
# counter.stub(:count) { 1 }
|
78
112
|
# counter.count # => 1
|
79
113
|
def and_return(*values, &implementation)
|
80
114
|
if negative?
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least)
|
85
|
-
|
86
|
-
if implementation
|
87
|
-
RSpec.deprecate('`and_return { value }`',
|
88
|
-
:replacement => '`and_return(value)` or an implementation block without `and_return`')
|
89
|
-
self.inner_implementation_action = implementation
|
115
|
+
raise "`and_return` is not supported with negative message expectations"
|
90
116
|
else
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
117
|
+
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least)
|
118
|
+
|
119
|
+
if implementation
|
120
|
+
# TODO: deprecate `and_return { value }`
|
121
|
+
self.inner_implementation_action = implementation
|
122
|
+
else
|
123
|
+
self.terminal_implementation_action = AndReturnImplementation.new(values)
|
95
124
|
end
|
96
125
|
|
97
|
-
|
126
|
+
nil
|
98
127
|
end
|
99
|
-
|
100
|
-
nil
|
101
128
|
end
|
102
129
|
|
103
130
|
def and_yield_receiver_to_implementation
|
@@ -121,9 +148,12 @@ module RSpec
|
|
121
148
|
# counter.increment
|
122
149
|
# expect(counter.count).to eq(original_count + 1)
|
123
150
|
def and_call_original
|
124
|
-
if
|
151
|
+
if RSpec::Mocks::TestDouble === @method_double.object
|
125
152
|
@error_generator.raise_only_valid_on_a_partial_mock(:and_call_original)
|
126
153
|
else
|
154
|
+
if implementation.inner_action
|
155
|
+
RSpec.warning("You're overriding a previous implementation for this stub")
|
156
|
+
end
|
127
157
|
@implementation = AndCallOriginalImplementation.new(@method_double.original_method)
|
128
158
|
@yield_receiver_to_implementation_block = false
|
129
159
|
end
|
@@ -181,7 +211,7 @@ module RSpec
|
|
181
211
|
#
|
182
212
|
# stream.stub(:open).and_yield(StringIO.new)
|
183
213
|
def and_yield(*args, &block)
|
184
|
-
yield @eval_context = Object.new
|
214
|
+
yield @eval_context = Object.new if block
|
185
215
|
@args_to_yield << args
|
186
216
|
self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
|
187
217
|
self
|
@@ -239,39 +269,21 @@ module RSpec
|
|
239
269
|
|
240
270
|
# @private
|
241
271
|
def verify_messages_received
|
242
|
-
|
243
|
-
|
244
|
-
error.backtrace.insert(0, @expected_from)
|
245
|
-
Kernel::raise error
|
246
|
-
end
|
247
|
-
|
248
|
-
# @private
|
249
|
-
def display_any_instance_deprecation_warning_if_necessary(block)
|
250
|
-
if passing_an_additional_arg_would_break_block?(block) &&
|
251
|
-
should_display_any_instance_deprecation_warning
|
252
|
-
line = if block.respond_to?(:source_location)
|
253
|
-
block.source_location.join(':')
|
254
|
-
else
|
255
|
-
@any_instance_source_line
|
256
|
-
end
|
257
|
-
|
258
|
-
display_any_instance_deprecation_warning(line)
|
259
|
-
@have_warned_about_yielding_receiver = true
|
272
|
+
InsertOntoBacktrace.line(@expected_from) do
|
273
|
+
generate_error unless expected_messages_received? || failed_fast?
|
260
274
|
end
|
261
275
|
end
|
262
276
|
|
263
|
-
# @private
|
264
|
-
def passing_an_additional_arg_would_break_block?(block)
|
265
|
-
return false unless block
|
266
|
-
return true if block.lambda?
|
267
|
-
!block.arity.zero?
|
268
|
-
end
|
269
|
-
|
270
277
|
# @private
|
271
278
|
def expected_messages_received?
|
272
279
|
ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
|
273
280
|
end
|
274
281
|
|
282
|
+
def ensure_expected_ordering_received!
|
283
|
+
@order_group.verify_invocation_order(self) if @ordered
|
284
|
+
true
|
285
|
+
end
|
286
|
+
|
275
287
|
# @private
|
276
288
|
def ignoring_args?
|
277
289
|
@expected_received_count == :any
|
@@ -351,18 +363,13 @@ module RSpec
|
|
351
363
|
# cart.add(Book.new(:isbn => 1934356379))
|
352
364
|
# # => passes
|
353
365
|
def with(*args, &block)
|
354
|
-
if
|
355
|
-
|
356
|
-
|
357
|
-
:replacement => "the `satisfy` matcher, a custom matcher or validate the arguments in an implementation block"
|
358
|
-
else
|
359
|
-
self.inner_implementation_action = block
|
360
|
-
end
|
361
|
-
elsif args.empty?
|
362
|
-
RSpec.deprecate "Using `with` without arguments", :replacement => "`with(no_args)`"
|
366
|
+
if args.empty?
|
367
|
+
raise ArgumentError,
|
368
|
+
"`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
|
363
369
|
end
|
364
370
|
|
365
|
-
|
371
|
+
self.inner_implementation_action = block
|
372
|
+
@argument_list_matcher = ArgumentListMatcher.new(*args)
|
366
373
|
self
|
367
374
|
end
|
368
375
|
|
@@ -385,12 +392,14 @@ module RSpec
|
|
385
392
|
#
|
386
393
|
# dealer.should_receive(:deal_card).at_least(9).times
|
387
394
|
def at_least(n, &block)
|
395
|
+
set_expected_received_count :at_least, n
|
396
|
+
|
388
397
|
if n == 0
|
389
|
-
|
398
|
+
raise "at_least(0) has been removed, use allow(...).to receive(:message) instead"
|
390
399
|
end
|
391
400
|
|
392
401
|
self.inner_implementation_action = block
|
393
|
-
|
402
|
+
|
394
403
|
self
|
395
404
|
end
|
396
405
|
|
@@ -418,15 +427,6 @@ module RSpec
|
|
418
427
|
self
|
419
428
|
end
|
420
429
|
|
421
|
-
|
422
|
-
# Allows an expected message to be received any number of times.
|
423
|
-
def any_number_of_times(&block)
|
424
|
-
RSpec.deprecate "any_number_of_times", :replacement => "stub"
|
425
|
-
self.inner_implementation_action = block
|
426
|
-
@expected_received_count = :any
|
427
|
-
self
|
428
|
-
end
|
429
|
-
|
430
430
|
# Expect a message not to be received at all.
|
431
431
|
#
|
432
432
|
# @example
|
@@ -469,11 +469,15 @@ module RSpec
|
|
469
469
|
# api.should_receive(:finish).ordered
|
470
470
|
def ordered(&block)
|
471
471
|
self.inner_implementation_action = block
|
472
|
-
@order_group.register(self)
|
473
472
|
@ordered = true
|
474
473
|
self
|
475
474
|
end
|
476
475
|
|
476
|
+
# @private
|
477
|
+
def ordered?
|
478
|
+
@ordered
|
479
|
+
end
|
480
|
+
|
477
481
|
# @private
|
478
482
|
def negative_expectation_for?(message)
|
479
483
|
@message == message && negative?
|
@@ -489,38 +493,6 @@ module RSpec
|
|
489
493
|
@actual_received_count += 1
|
490
494
|
end
|
491
495
|
|
492
|
-
def warn_about_receiver_passing(any_instance_source_line)
|
493
|
-
@any_instance_source_line = any_instance_source_line
|
494
|
-
@warn_about_yielding_receiver_to_implementation_block = true
|
495
|
-
end
|
496
|
-
|
497
|
-
def should_display_any_instance_deprecation_warning
|
498
|
-
warn_about_yielding_receiver_to_implementation_block &&
|
499
|
-
!@have_warned_about_yielding_receiver
|
500
|
-
end
|
501
|
-
|
502
|
-
def display_any_instance_deprecation_warning(block_source_line)
|
503
|
-
RSpec.warn_deprecation(<<MSG
|
504
|
-
In RSpec 3, `any_instance` implementation blocks will be yielded the receiving
|
505
|
-
instance as the first block argument to allow the implementation block to use
|
506
|
-
the state of the receiver. To maintain compatibility with RSpec 3 you need to
|
507
|
-
either set rspec-mocks' `yield_receiver_to_any_instance_implementation_blocks`
|
508
|
-
config option to `false` OR set it to `true` and update your `any_instance`
|
509
|
-
implementation blocks to account for the first block argument being the receiving instance.
|
510
|
-
|
511
|
-
To set the config option, use a snippet like:
|
512
|
-
|
513
|
-
RSpec.configure do |rspec|
|
514
|
-
rspec.mock_with :rspec do |mocks|
|
515
|
-
mocks.yield_receiver_to_any_instance_implementation_blocks = false
|
516
|
-
end
|
517
|
-
end
|
518
|
-
|
519
|
-
Your `any_instance` implementation block is declared at: #{block_source_line}
|
520
|
-
MSG
|
521
|
-
)
|
522
|
-
end
|
523
|
-
|
524
496
|
private
|
525
497
|
|
526
498
|
def failed_fast?
|
@@ -543,7 +515,7 @@ MSG
|
|
543
515
|
end
|
544
516
|
|
545
517
|
def inner_implementation_action=(action)
|
546
|
-
|
518
|
+
RSpec.warning("You're overriding a previous implementation for this stub") if implementation.inner_action
|
547
519
|
implementation.inner_action = action if action
|
548
520
|
end
|
549
521
|
|
@@ -561,10 +533,6 @@ MSG
|
|
561
533
|
@error_generator = error_generator
|
562
534
|
end
|
563
535
|
|
564
|
-
def arity
|
565
|
-
-1
|
566
|
-
end
|
567
|
-
|
568
536
|
def call(*args_to_ignore, &block)
|
569
537
|
return if @args_to_yield.empty? && @eval_context.nil?
|
570
538
|
|
@@ -587,10 +555,6 @@ MSG
|
|
587
555
|
@values_to_return = values_to_return
|
588
556
|
end
|
589
557
|
|
590
|
-
def arity
|
591
|
-
-1
|
592
|
-
end
|
593
|
-
|
594
558
|
def call(*args_to_ignore, &block)
|
595
559
|
if @values_to_return.size > 1
|
596
560
|
@values_to_return.shift
|
@@ -608,39 +572,10 @@ MSG
|
|
608
572
|
|
609
573
|
def call(*args, &block)
|
610
574
|
actions.map do |action|
|
611
|
-
|
612
|
-
RSpec.deprecate "stubbing implementations with mismatched arity",
|
613
|
-
:call_site => CallerFilter.first_non_rspec_line
|
614
|
-
end
|
615
|
-
action.call(*arg_slice_for(args, action.arity), &block)
|
575
|
+
action.call(*args, &block)
|
616
576
|
end.last
|
617
577
|
end
|
618
578
|
|
619
|
-
if RUBY_VERSION.to_f > 1.8
|
620
|
-
def arg_slice_for(args, arity)
|
621
|
-
if arity >= 0
|
622
|
-
args.slice(0, arity)
|
623
|
-
else
|
624
|
-
args
|
625
|
-
end
|
626
|
-
end
|
627
|
-
else
|
628
|
-
# 1.8.7's `arity` lies somtimes:
|
629
|
-
# Given:
|
630
|
-
# def print_arity(&b) puts b.arity; end
|
631
|
-
#
|
632
|
-
# This prints 1:
|
633
|
-
# print_arity { |a, b, c, &bl| }
|
634
|
-
#
|
635
|
-
# But this prints 3:
|
636
|
-
# print_arity { |a, b, c| }
|
637
|
-
#
|
638
|
-
# Given that it lies, we can't trust it and we don't slice the args.
|
639
|
-
def arg_slice_for(args, arity)
|
640
|
-
args
|
641
|
-
end
|
642
|
-
end
|
643
|
-
|
644
579
|
def present?
|
645
580
|
actions.any?
|
646
581
|
end
|
@@ -661,10 +596,6 @@ MSG
|
|
661
596
|
|
662
597
|
CannotModifyFurtherError = Class.new(StandardError)
|
663
598
|
|
664
|
-
def arity
|
665
|
-
@method.arity
|
666
|
-
end
|
667
|
-
|
668
599
|
def initial_action=(value)
|
669
600
|
raise cannot_modify_further_error
|
670
601
|
end
|
@@ -681,6 +612,10 @@ MSG
|
|
681
612
|
true
|
682
613
|
end
|
683
614
|
|
615
|
+
def inner_action
|
616
|
+
true
|
617
|
+
end
|
618
|
+
|
684
619
|
def call(*args, &block)
|
685
620
|
@method.call(*args, &block)
|
686
621
|
end
|
@@ -692,5 +627,17 @@ MSG
|
|
692
627
|
"to call the original implementation, and cannot be modified further."
|
693
628
|
end
|
694
629
|
end
|
630
|
+
|
631
|
+
# Insert original locations into stacktraces
|
632
|
+
# @api private
|
633
|
+
class InsertOntoBacktrace
|
634
|
+
def self.line(location)
|
635
|
+
yield
|
636
|
+
rescue RSpec::Mocks::MockExpectationError => error
|
637
|
+
error.backtrace.insert(0, location)
|
638
|
+
Kernel::raise error
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
695
642
|
end
|
696
643
|
end
|