rspec-mocks 2.99.4 → 3.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|