rspec-mocks 2.11.3 → 3.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +1 -1
  5. data/Changelog.md +989 -21
  6. data/{License.txt → LICENSE.md} +5 -3
  7. data/README.md +260 -73
  8. data/lib/rspec/mocks/any_instance/chain.rb +58 -24
  9. data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
  10. data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +31 -0
  11. data/lib/rspec/mocks/any_instance/expectation_chain.rb +23 -30
  12. data/lib/rspec/mocks/any_instance/message_chains.rb +38 -15
  13. data/lib/rspec/mocks/any_instance/proxy.rb +116 -0
  14. data/lib/rspec/mocks/any_instance/recorder.rb +155 -59
  15. data/lib/rspec/mocks/any_instance/stub_chain.rb +33 -19
  16. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +10 -12
  17. data/lib/rspec/mocks/any_instance.rb +11 -81
  18. data/lib/rspec/mocks/argument_list_matcher.rb +59 -37
  19. data/lib/rspec/mocks/argument_matchers.rb +233 -149
  20. data/lib/rspec/mocks/configuration.rb +212 -0
  21. data/lib/rspec/mocks/error_generator.rb +304 -49
  22. data/lib/rspec/mocks/example_methods.rb +361 -22
  23. data/lib/rspec/mocks/instance_method_stasher.rb +146 -0
  24. data/lib/rspec/mocks/marshal_extension.rb +41 -0
  25. data/lib/rspec/mocks/matchers/expectation_customization.rb +20 -0
  26. data/lib/rspec/mocks/matchers/have_received.rb +134 -0
  27. data/lib/rspec/mocks/matchers/receive.rb +133 -0
  28. data/lib/rspec/mocks/matchers/receive_message_chain.rb +82 -0
  29. data/lib/rspec/mocks/matchers/receive_messages.rb +77 -0
  30. data/lib/rspec/mocks/message_chain.rb +87 -0
  31. data/lib/rspec/mocks/message_expectation.rb +648 -314
  32. data/lib/rspec/mocks/method_double.rb +185 -58
  33. data/lib/rspec/mocks/method_reference.rb +202 -0
  34. data/lib/rspec/mocks/minitest_integration.rb +68 -0
  35. data/lib/rspec/mocks/mutate_const.rb +339 -0
  36. data/lib/rspec/mocks/object_reference.rb +149 -0
  37. data/lib/rspec/mocks/order_group.rb +48 -7
  38. data/lib/rspec/mocks/proxy.rb +405 -74
  39. data/lib/rspec/mocks/space.rb +219 -15
  40. data/lib/rspec/mocks/standalone.rb +2 -2
  41. data/lib/rspec/mocks/syntax.rb +325 -0
  42. data/lib/rspec/mocks/targets.rb +124 -0
  43. data/lib/rspec/mocks/test_double.rb +125 -57
  44. data/lib/rspec/mocks/verifying_double.rb +121 -0
  45. data/lib/rspec/mocks/verifying_message_expectation.rb +54 -0
  46. data/lib/rspec/mocks/verifying_proxy.rb +220 -0
  47. data/lib/rspec/mocks/version.rb +3 -1
  48. data/lib/rspec/mocks.rb +121 -27
  49. data.tar.gz.sig +0 -0
  50. metadata +178 -253
  51. metadata.gz.sig +3 -0
  52. data/features/README.md +0 -67
  53. data/features/Scope.md +0 -17
  54. data/features/Upgrade.md +0 -22
  55. data/features/argument_matchers/README.md +0 -27
  56. data/features/argument_matchers/explicit.feature +0 -60
  57. data/features/argument_matchers/general_matchers.feature +0 -85
  58. data/features/argument_matchers/type_matchers.feature +0 -27
  59. data/features/message_expectations/README.md +0 -69
  60. data/features/message_expectations/any_instance.feature +0 -21
  61. data/features/message_expectations/block_local_expectations.feature.pending +0 -55
  62. data/features/message_expectations/expect_message.feature +0 -94
  63. data/features/message_expectations/receive_counts.feature +0 -209
  64. data/features/message_expectations/warn_when_expectation_is_set_on_nil.feature +0 -50
  65. data/features/method_stubs/README.md +0 -47
  66. data/features/method_stubs/any_instance.feature +0 -133
  67. data/features/method_stubs/as_null_object.feature +0 -35
  68. data/features/method_stubs/simple_return_value.feature +0 -64
  69. data/features/method_stubs/stub_chain.feature +0 -51
  70. data/features/method_stubs/stub_implementation.feature +0 -26
  71. data/features/method_stubs/to_ary.feature +0 -47
  72. data/features/outside_rspec/configuration.feature +0 -82
  73. data/features/outside_rspec/standalone.feature +0 -32
  74. data/features/step_definitions/additional_cli_steps.rb +0 -4
  75. data/features/stubbing_constants/README.md +0 -62
  76. data/features/stubbing_constants/stub_defined_constant.feature +0 -79
  77. data/features/stubbing_constants/stub_undefined_constant.feature +0 -50
  78. data/features/support/env.rb +0 -6
  79. data/lib/rspec/mocks/errors.rb +0 -12
  80. data/lib/rspec/mocks/extensions/instance_exec.rb +0 -34
  81. data/lib/rspec/mocks/extensions/marshal.rb +0 -23
  82. data/lib/rspec/mocks/extensions/psych.rb +0 -23
  83. data/lib/rspec/mocks/framework.rb +0 -21
  84. data/lib/rspec/mocks/methods.rb +0 -155
  85. data/lib/rspec/mocks/mock.rb +0 -7
  86. data/lib/rspec/mocks/serialization.rb +0 -34
  87. data/lib/rspec/mocks/stashed_instance_method.rb +0 -60
  88. data/lib/rspec/mocks/stub_const.rb +0 -332
  89. data/lib/spec/mocks.rb +0 -2
  90. data/spec/rspec/mocks/and_yield_spec.rb +0 -114
  91. data/spec/rspec/mocks/any_instance/message_chains_spec.rb +0 -40
  92. data/spec/rspec/mocks/any_instance_spec.rb +0 -877
  93. data/spec/rspec/mocks/any_number_of_times_spec.rb +0 -30
  94. data/spec/rspec/mocks/argument_expectation_spec.rb +0 -34
  95. data/spec/rspec/mocks/at_least_spec.rb +0 -142
  96. data/spec/rspec/mocks/at_most_spec.rb +0 -90
  97. data/spec/rspec/mocks/block_return_value_spec.rb +0 -53
  98. data/spec/rspec/mocks/bug_report_10260_spec.rb +0 -8
  99. data/spec/rspec/mocks/bug_report_10263_spec.rb +0 -25
  100. data/spec/rspec/mocks/bug_report_11545_spec.rb +0 -32
  101. data/spec/rspec/mocks/bug_report_496_spec.rb +0 -17
  102. data/spec/rspec/mocks/bug_report_600_spec.rb +0 -22
  103. data/spec/rspec/mocks/bug_report_7611_spec.rb +0 -16
  104. data/spec/rspec/mocks/bug_report_8165_spec.rb +0 -31
  105. data/spec/rspec/mocks/bug_report_830_spec.rb +0 -21
  106. data/spec/rspec/mocks/bug_report_957_spec.rb +0 -22
  107. data/spec/rspec/mocks/double_spec.rb +0 -12
  108. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +0 -95
  109. data/spec/rspec/mocks/hash_excluding_matcher_spec.rb +0 -67
  110. data/spec/rspec/mocks/hash_including_matcher_spec.rb +0 -90
  111. data/spec/rspec/mocks/mock_ordering_spec.rb +0 -103
  112. data/spec/rspec/mocks/mock_space_spec.rb +0 -58
  113. data/spec/rspec/mocks/mock_spec.rb +0 -730
  114. data/spec/rspec/mocks/multiple_return_value_spec.rb +0 -119
  115. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +0 -62
  116. data/spec/rspec/mocks/null_object_mock_spec.rb +0 -106
  117. data/spec/rspec/mocks/once_counts_spec.rb +0 -52
  118. data/spec/rspec/mocks/options_hash_spec.rb +0 -35
  119. data/spec/rspec/mocks/partial_mock_spec.rb +0 -171
  120. data/spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb +0 -95
  121. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +0 -142
  122. data/spec/rspec/mocks/precise_counts_spec.rb +0 -68
  123. data/spec/rspec/mocks/record_messages_spec.rb +0 -26
  124. data/spec/rspec/mocks/serialization_spec.rb +0 -111
  125. data/spec/rspec/mocks/stash_spec.rb +0 -27
  126. data/spec/rspec/mocks/stashed_instance_method_spec.rb +0 -53
  127. data/spec/rspec/mocks/stub_chain_spec.rb +0 -154
  128. data/spec/rspec/mocks/stub_const_spec.rb +0 -334
  129. data/spec/rspec/mocks/stub_implementation_spec.rb +0 -81
  130. data/spec/rspec/mocks/stub_spec.rb +0 -247
  131. data/spec/rspec/mocks/stubbed_message_expectations_spec.rb +0 -47
  132. data/spec/rspec/mocks/test_double_spec.rb +0 -57
  133. data/spec/rspec/mocks/to_ary_spec.rb +0 -40
  134. data/spec/rspec/mocks/twice_counts_spec.rb +0 -66
  135. data/spec/rspec/mocks_spec.rb +0 -51
  136. data/spec/spec_helper.rb +0 -21
@@ -1,28 +1,32 @@
1
1
  module RSpec
2
2
  module Mocks
3
- # Implements the methods needed for a pure test double. RSpec::Mocks::Mock
3
+ # Implements the methods needed for a pure test double. RSpec::Mocks::Double
4
4
  # includes this module, and it is provided for cases where you want a
5
- # pure test double without subclassing RSpec::Mocks::Mock.
5
+ # pure test double without subclassing RSpec::Mocks::Double.
6
6
  module TestDouble
7
- include Methods
7
+ # Creates a new test double with a `name` (that will be used in error
8
+ # messages only)
9
+ def initialize(name=nil, stubs={})
10
+ @__expired = false
11
+ if Hash === name && stubs.empty?
12
+ stubs = name
13
+ @name = nil
14
+ else
15
+ @name = name
16
+ end
17
+ assign_stubs(stubs)
18
+ end
8
19
 
9
- # Extends the TestDouble module onto the given object and
10
- # initializes it as a test double.
11
- #
12
- # @example
13
- #
14
- # module = Module.new
15
- # RSpec::Mocks::TestDouble.extend_onto(module, "MyMixin", :foo => "bar")
16
- # module.foo #=> "bar"
17
- def self.extend_onto(object, name=nil, stubs_and_options={})
18
- object.extend self
19
- object.send(:__initialize_as_test_double, name, stubs_and_options)
20
+ # Tells the object to respond to all messages. If specific stub values
21
+ # are declared, they'll work as expected. If not, the receiver is
22
+ # returned.
23
+ def as_null_object
24
+ __mock_proxy.as_null_object
20
25
  end
21
26
 
22
- # Creates a new test double with a `name` (that will be used in error
23
- # messages only)
24
- def initialize(name=nil, stubs_and_options={})
25
- __initialize_as_test_double(name, stubs_and_options)
27
+ # Returns true if this object has received `as_null_object`
28
+ def null_object?
29
+ __mock_proxy.null_object?
26
30
  end
27
31
 
28
32
  # This allows for comparing the mock to other objects that proxy such as
@@ -35,67 +39,131 @@ module RSpec
35
39
 
36
40
  # @private
37
41
  def inspect
38
- "#<#{self.class}:#{sprintf '0x%x', self.object_id} @name=#{@name.inspect}>"
42
+ TestDoubleFormatter.format(self)
39
43
  end
40
44
 
41
45
  # @private
42
46
  def to_s
43
- inspect.gsub('<','[').gsub('>',']')
47
+ inspect.tr('<', '[').tr('>', ']')
44
48
  end
45
49
 
46
- alias_method :to_str, :to_s
47
-
48
50
  # @private
49
51
  def respond_to?(message, incl_private=false)
50
- __mock_proxy.null_object? && message != :to_ary ? true : super
52
+ __mock_proxy.null_object? ? true : super
51
53
  end
52
54
 
53
- private
55
+ # @private
56
+ def __build_mock_proxy_unless_expired(order_group)
57
+ __raise_expired_error || __build_mock_proxy(order_group)
58
+ end
54
59
 
55
- def __initialize_as_test_double(name=nil, stubs_and_options={})
56
- if name.is_a?(Hash) && stubs_and_options.empty?
57
- stubs_and_options = name
58
- @name = nil
59
- else
60
- @name = name
61
- end
62
- @options = extract_options(stubs_and_options)
63
- assign_stubs(stubs_and_options)
60
+ # @private
61
+ def __disallow_further_usage!
62
+ @__expired = true
64
63
  end
65
64
 
66
- def method_missing(message, *args, &block)
67
- raise NoMethodError if message == :to_ary
68
- return 0 if message == :to_int && __mock_proxy.null_object?
69
- __mock_proxy.record_message_received(message, *args, &block)
70
-
71
- begin
72
- __mock_proxy.null_object? ? self : super
73
- rescue NameError
74
- __mock_proxy.raise_unexpected_message_error(message, *args)
75
- end
65
+ # Override for default freeze implementation to prevent freezing of test
66
+ # doubles.
67
+ def freeze
68
+ RSpec.warn_with("WARNING: you attempted to freeze a test double. This is explicitly a no-op as freezing doubles can lead to undesired behaviour when resetting tests.")
69
+ self
76
70
  end
77
71
 
78
- def extract_options(stubs_and_options)
79
- if stubs_and_options[:null_object]
80
- @null_object = stubs_and_options.delete(:null_object)
81
- RSpec.deprecate(%Q["double('name', :null_object => true)"], %Q["double('name').as_null_object"])
72
+ private
73
+
74
+ def method_missing(message, *args, &block)
75
+ proxy = __mock_proxy
76
+ proxy.record_message_received(message, *args, &block)
77
+
78
+ if proxy.null_object?
79
+ case message
80
+ when :to_int then return 0
81
+ when :to_a, :to_ary then return nil
82
+ when :to_str then return to_s
83
+ else return self
84
+ end
82
85
  end
83
- options = {}
84
- extract_option(stubs_and_options, options, :__declared_as, 'Mock')
85
- options
86
- end
87
86
 
88
- def extract_option(source, target, key, default=nil)
89
- if source[key]
90
- target[key] = source.delete(key)
91
- elsif default
92
- target[key] = default
87
+ # Defined private and protected methods will still trigger `method_missing`
88
+ # when called publicly. We want ruby's method visibility error to get raised,
89
+ # so we simply delegate to `super` in that case.
90
+ # ...well, we would delegate to `super`, but there's a JRuby
91
+ # bug, so we raise our own visibility error instead:
92
+ # https://github.com/jruby/jruby/issues/1398
93
+ visibility = proxy.visibility_for(message)
94
+ if visibility == :private || visibility == :protected
95
+ ErrorGenerator.new(self).raise_non_public_error(
96
+ message, visibility
97
+ )
93
98
  end
99
+
100
+ # Required wrapping doubles in an Array on Ruby 1.9.2
101
+ raise NoMethodError if [:to_a, :to_ary].include? message
102
+ proxy.raise_unexpected_message_error(message, args)
94
103
  end
95
104
 
96
105
  def assign_stubs(stubs)
97
106
  stubs.each_pair do |message, response|
98
- stub(message).and_return(response)
107
+ __mock_proxy.add_simple_stub(message, response)
108
+ end
109
+ end
110
+
111
+ def __mock_proxy
112
+ ::RSpec::Mocks.space.proxy_for(self)
113
+ end
114
+
115
+ def __build_mock_proxy(order_group)
116
+ TestDoubleProxy.new(self, order_group)
117
+ end
118
+
119
+ def __raise_expired_error
120
+ return false unless @__expired
121
+ ErrorGenerator.new(self).raise_expired_test_double_error
122
+ end
123
+
124
+ def initialize_copy(other)
125
+ as_null_object if other.null_object?
126
+ super
127
+ end
128
+ end
129
+
130
+ # A generic test double object. `double`, `instance_double` and friends
131
+ # return an instance of this.
132
+ class Double
133
+ include TestDouble
134
+ end
135
+
136
+ # @private
137
+ module TestDoubleFormatter
138
+ def self.format(dbl, unwrap=false)
139
+ format = "#{type_desc(dbl)}#{verified_module_desc(dbl)} #{name_desc(dbl)}"
140
+ return format if unwrap
141
+ "#<#{format}>"
142
+ end
143
+
144
+ class << self
145
+ private
146
+
147
+ def type_desc(dbl)
148
+ case dbl
149
+ when InstanceVerifyingDouble then "InstanceDouble"
150
+ when ClassVerifyingDouble then "ClassDouble"
151
+ when ObjectVerifyingDouble then "ObjectDouble"
152
+ else "Double"
153
+ end
154
+ end
155
+
156
+ # @private
157
+ IVAR_GET = Object.instance_method(:instance_variable_get)
158
+
159
+ def verified_module_desc(dbl)
160
+ return nil unless VerifyingDouble === dbl
161
+ "(#{IVAR_GET.bind(dbl).call(:@doubled_module).description})"
162
+ end
163
+
164
+ def name_desc(dbl)
165
+ return "(anonymous)" unless (name = IVAR_GET.bind(dbl).call(:@name))
166
+ name.inspect
99
167
  end
100
168
  end
101
169
  end
@@ -0,0 +1,121 @@
1
+ RSpec::Support.require_rspec_mocks 'verifying_proxy'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ # @private
6
+ module VerifyingDouble
7
+ def respond_to?(message, include_private=false)
8
+ return super unless null_object?
9
+
10
+ method_ref = __mock_proxy.method_reference[message]
11
+
12
+ case method_ref.visibility
13
+ when :public then true
14
+ when :private then include_private
15
+ when :protected then include_private || RUBY_VERSION.to_f < 2.0
16
+ else !method_ref.unimplemented?
17
+ end
18
+ end
19
+
20
+ def method_missing(message, *args, &block)
21
+ # Null object conditional is an optimization. If not a null object,
22
+ # validity of method expectations will have been checked at definition
23
+ # time.
24
+ if null_object?
25
+ if @__sending_message == message
26
+ __mock_proxy.ensure_implemented(message)
27
+ else
28
+ __mock_proxy.ensure_publicly_implemented(message, self)
29
+ end
30
+
31
+ __mock_proxy.validate_arguments!(message, args)
32
+ end
33
+
34
+ super
35
+ end
36
+
37
+ # Redefining `__send__` causes ruby to issue a warning.
38
+ old, $VERBOSE = $VERBOSE, nil
39
+ def __send__(name, *args, &block)
40
+ @__sending_message = name
41
+ super
42
+ ensure
43
+ @__sending_message = nil
44
+ end
45
+ $VERBOSE = old
46
+
47
+ def send(name, *args, &block)
48
+ __send__(name, *args, &block)
49
+ end
50
+
51
+ def initialize(doubled_module, *args)
52
+ @doubled_module = doubled_module
53
+
54
+ possible_name = args.first
55
+ name = if String === possible_name || Symbol === possible_name
56
+ args.shift
57
+ end
58
+
59
+ super(name, *args)
60
+ @__sending_message = nil
61
+ end
62
+ end
63
+
64
+ # A mock providing a custom proxy that can verify the validity of any
65
+ # method stubs or expectations against the public instance methods of the
66
+ # given class.
67
+ #
68
+ # @private
69
+ class InstanceVerifyingDouble
70
+ include TestDouble
71
+ include VerifyingDouble
72
+
73
+ def __build_mock_proxy(order_group)
74
+ VerifyingProxy.new(self, order_group,
75
+ @doubled_module,
76
+ InstanceMethodReference
77
+ )
78
+ end
79
+ end
80
+
81
+ # An awkward module necessary because we cannot otherwise have
82
+ # ClassVerifyingDouble inherit from Module and still share these methods.
83
+ #
84
+ # @private
85
+ module ObjectVerifyingDoubleMethods
86
+ include TestDouble
87
+ include VerifyingDouble
88
+
89
+ def as_stubbed_const(options={})
90
+ ConstantMutator.stub(@doubled_module.const_to_replace, self, options)
91
+ self
92
+ end
93
+
94
+ private
95
+
96
+ def __build_mock_proxy(order_group)
97
+ VerifyingProxy.new(self, order_group,
98
+ @doubled_module,
99
+ ObjectMethodReference
100
+ )
101
+ end
102
+ end
103
+
104
+ # Similar to an InstanceVerifyingDouble, except that it verifies against
105
+ # public methods of the given object.
106
+ #
107
+ # @private
108
+ class ObjectVerifyingDouble
109
+ include ObjectVerifyingDoubleMethods
110
+ end
111
+
112
+ # Effectively the same as an ObjectVerifyingDouble (since a class is a type
113
+ # of object), except with Module in the inheritance chain so that
114
+ # transferring nested constants to work.
115
+ #
116
+ # @private
117
+ class ClassVerifyingDouble < Module
118
+ include ObjectVerifyingDoubleMethods
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,54 @@
1
+ RSpec::Support.require_rspec_support 'method_signature_verifier'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ # A message expectation that knows about the real implementation of the
6
+ # message being expected, so that it can verify that any expectations
7
+ # have the valid arguments.
8
+ # @api private
9
+ class VerifyingMessageExpectation < MessageExpectation
10
+ # A level of indirection is used here rather than just passing in the
11
+ # method itself, since method look up is expensive and we only want to
12
+ # do it if actually needed.
13
+ #
14
+ # Conceptually the method reference makes more sense as a constructor
15
+ # argument since it should be immutable, but it is significantly more
16
+ # straight forward to build the object in pieces so for now it stays as
17
+ # an accessor.
18
+ attr_accessor :method_reference
19
+
20
+ def initialize(*args)
21
+ super
22
+ end
23
+
24
+ # @private
25
+ def with(*args, &block)
26
+ super(*args, &block).tap do
27
+ validate_expected_arguments! do |signature|
28
+ example_call_site_args = [:an_arg] * signature.min_non_kw_args
29
+ example_call_site_args << :kw_args_hash if signature.required_kw_args.any?
30
+ @argument_list_matcher.resolve_expected_args_based_on(example_call_site_args)
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def validate_expected_arguments!
38
+ return if method_reference.nil?
39
+
40
+ method_reference.with_signature do |signature|
41
+ args = yield signature
42
+ verifier = Support::LooseSignatureVerifier.new(signature, args)
43
+
44
+ unless verifier.valid?
45
+ # Fail fast is required, otherwise the message expectation will fail
46
+ # as well ("expected method not called") and clobber this one.
47
+ @failed_fast = true
48
+ @error_generator.raise_invalid_arguments_error(verifier)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,220 @@
1
+ RSpec::Support.require_rspec_mocks 'verifying_message_expectation'
2
+ RSpec::Support.require_rspec_mocks 'method_reference'
3
+
4
+ module RSpec
5
+ module Mocks
6
+ # @private
7
+ class CallbackInvocationStrategy
8
+ def call(doubled_module)
9
+ RSpec::Mocks.configuration.verifying_double_callbacks.each do |block|
10
+ block.call doubled_module
11
+ end
12
+ end
13
+ end
14
+
15
+ # @private
16
+ class NoCallbackInvocationStrategy
17
+ def call(_doubled_module)
18
+ end
19
+ end
20
+
21
+ # @private
22
+ module VerifyingProxyMethods
23
+ def add_stub(method_name, opts={}, &implementation)
24
+ ensure_implemented(method_name)
25
+ super
26
+ end
27
+
28
+ def add_simple_stub(method_name, *args)
29
+ ensure_implemented(method_name)
30
+ super
31
+ end
32
+
33
+ def add_message_expectation(method_name, opts={}, &block)
34
+ ensure_implemented(method_name)
35
+ super
36
+ end
37
+
38
+ def ensure_implemented(method_name)
39
+ return unless method_reference[method_name].unimplemented?
40
+
41
+ @error_generator.raise_unimplemented_error(
42
+ @doubled_module,
43
+ method_name,
44
+ @object
45
+ )
46
+ end
47
+
48
+ def ensure_publicly_implemented(method_name, _object)
49
+ ensure_implemented(method_name)
50
+ visibility = method_reference[method_name].visibility
51
+
52
+ return if visibility == :public
53
+ @error_generator.raise_non_public_error(method_name, visibility)
54
+ end
55
+ end
56
+
57
+ # A verifying proxy mostly acts like a normal proxy, except that it
58
+ # contains extra logic to try and determine the validity of any expectation
59
+ # set on it. This includes whether or not methods have been defined and the
60
+ # validatiy of arguments on method calls.
61
+ #
62
+ # In all other ways this behaves like a normal proxy. It only adds the
63
+ # verification behaviour to specific methods then delegates to the parent
64
+ # implementation.
65
+ #
66
+ # These checks are only activated if the doubled class has already been
67
+ # loaded, otherwise they are disabled. This allows for testing in
68
+ # isolation.
69
+ #
70
+ # @private
71
+ class VerifyingProxy < TestDoubleProxy
72
+ include VerifyingProxyMethods
73
+
74
+ def initialize(object, order_group, doubled_module, method_reference_class)
75
+ super(object, order_group)
76
+ @object = object
77
+ @doubled_module = doubled_module
78
+ @method_reference_class = method_reference_class
79
+
80
+ # A custom method double is required to pass through a way to lookup
81
+ # methods to determine their parameters. This is only relevant if the doubled
82
+ # class is loaded.
83
+ @method_doubles = Hash.new do |h, k|
84
+ h[k] = VerifyingMethodDouble.new(@object, k, self, method_reference[k])
85
+ end
86
+ end
87
+
88
+ def method_reference
89
+ @method_reference ||= Hash.new do |h, k|
90
+ h[k] = @method_reference_class.for(@doubled_module, k)
91
+ end
92
+ end
93
+
94
+ def visibility_for(method_name)
95
+ method_reference[method_name].visibility
96
+ end
97
+
98
+ def validate_arguments!(method_name, args)
99
+ @method_doubles[method_name].validate_arguments!(args)
100
+ end
101
+ end
102
+
103
+ # @private
104
+ DEFAULT_CALLBACK_INVOCATION_STRATEGY = CallbackInvocationStrategy.new
105
+
106
+ # @private
107
+ class VerifyingPartialDoubleProxy < PartialDoubleProxy
108
+ include VerifyingProxyMethods
109
+
110
+ def initialize(object, expectation_ordering, optional_callback_invocation_strategy=DEFAULT_CALLBACK_INVOCATION_STRATEGY)
111
+ super(object, expectation_ordering)
112
+ @doubled_module = DirectObjectReference.new(object)
113
+
114
+ # A custom method double is required to pass through a way to lookup
115
+ # methods to determine their parameters.
116
+ @method_doubles = Hash.new do |h, k|
117
+ h[k] = VerifyingExistingMethodDouble.for(object, k, self)
118
+ end
119
+
120
+ optional_callback_invocation_strategy.call(@doubled_module)
121
+ end
122
+
123
+ def ensure_implemented(_method_name)
124
+ return if Mocks.configuration.temporarily_suppress_partial_double_verification
125
+ super
126
+ end
127
+
128
+ def method_reference
129
+ @method_doubles
130
+ end
131
+ end
132
+
133
+ # @private
134
+ class VerifyingPartialClassDoubleProxy < VerifyingPartialDoubleProxy
135
+ include PartialClassDoubleProxyMethods
136
+ end
137
+
138
+ # @private
139
+ class VerifyingMethodDouble < MethodDouble
140
+ def initialize(object, method_name, proxy, method_reference)
141
+ super(object, method_name, proxy)
142
+ @method_reference = method_reference
143
+ end
144
+
145
+ def message_expectation_class
146
+ VerifyingMessageExpectation
147
+ end
148
+
149
+ def add_expectation(*args, &block)
150
+ # explict params necessary for 1.8.7 see #626
151
+ super(*args, &block).tap { |x| x.method_reference = @method_reference }
152
+ end
153
+
154
+ def add_stub(*args, &block)
155
+ # explict params necessary for 1.8.7 see #626
156
+ super(*args, &block).tap { |x| x.method_reference = @method_reference }
157
+ end
158
+
159
+ def proxy_method_invoked(obj, *args, &block)
160
+ validate_arguments!(args)
161
+ super
162
+ end
163
+
164
+ def validate_arguments!(actual_args)
165
+ @method_reference.with_signature do |signature|
166
+ verifier = Support::StrictSignatureVerifier.new(signature, actual_args)
167
+ raise ArgumentError, verifier.error_message unless verifier.valid?
168
+ end
169
+ end
170
+ end
171
+
172
+ # A VerifyingMethodDouble fetches the method to verify against from the
173
+ # original object, using a MethodReference. This works for pure doubles,
174
+ # but when the original object is itself the one being modified we need to
175
+ # collapse the reference and the method double into a single object so that
176
+ # we can access the original pristine method definition.
177
+ #
178
+ # @private
179
+ class VerifyingExistingMethodDouble < VerifyingMethodDouble
180
+ def initialize(object, method_name, proxy)
181
+ super(object, method_name, proxy, self)
182
+
183
+ @valid_method = object.respond_to?(method_name, true)
184
+
185
+ # Trigger an eager find of the original method since if we find it any
186
+ # later we end up getting a stubbed method with incorrect arity.
187
+ save_original_implementation_callable!
188
+ end
189
+
190
+ def with_signature
191
+ yield Support::MethodSignature.new(original_implementation_callable)
192
+ end
193
+
194
+ def unimplemented?
195
+ !@valid_method
196
+ end
197
+
198
+ def self.for(object, method_name, proxy)
199
+ if ClassNewMethodReference.applies_to?(method_name) { object }
200
+ VerifyingExistingClassNewMethodDouble
201
+ elsif Mocks.configuration.temporarily_suppress_partial_double_verification
202
+ MethodDouble
203
+ else
204
+ self
205
+ end.new(object, method_name, proxy)
206
+ end
207
+ end
208
+
209
+ # Used in place of a `VerifyingExistingMethodDouble` for the specific case
210
+ # of mocking or stubbing a `new` method on a class. In this case, we substitute
211
+ # the method signature from `#initialize` since new's signature is just `*args`.
212
+ #
213
+ # @private
214
+ class VerifyingExistingClassNewMethodDouble < VerifyingExistingMethodDouble
215
+ def with_signature
216
+ yield Support::MethodSignature.new(object.instance_method(:initialize))
217
+ end
218
+ end
219
+ end
220
+ end
@@ -1,7 +1,9 @@
1
1
  module RSpec
2
2
  module Mocks
3
+ # Version information for RSpec mocks.
3
4
  module Version
4
- STRING = '2.11.3'
5
+ # Version of RSpec mocks currently in use in SemVer format.
6
+ STRING = '3.11.1'
5
7
  end
6
8
  end
7
9
  end