mocha 1.12.0 → 2.1.0
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 +4 -4
- data/.rubocop.yml +5 -13
- data/.yardopts +0 -1
- data/CONTRIBUTING.md +1 -1
- data/COPYING.md +2 -2
- data/Gemfile +27 -0
- data/MIT-LICENSE.md +1 -1
- data/README.md +12 -8
- data/RELEASE.md +169 -0
- data/Rakefile +22 -29
- data/gemfiles/Gemfile.minitest.latest +1 -0
- data/gemfiles/Gemfile.test-unit.latest +2 -5
- data/lib/mocha/any_instance_method.rb +0 -5
- data/lib/mocha/api.rb +29 -78
- data/lib/mocha/backtrace_filter.rb +2 -2
- data/lib/mocha/class_methods.rb +2 -2
- data/lib/mocha/configuration.rb +36 -114
- data/lib/mocha/debug.rb +2 -5
- data/lib/mocha/expectation.rb +63 -8
- data/lib/mocha/inspect.rb +6 -4
- data/lib/mocha/instance_method.rb +0 -4
- data/lib/mocha/integration/mini_test/adapter.rb +1 -1
- data/lib/mocha/integration/mini_test.rb +10 -38
- data/lib/mocha/integration/test_unit/adapter.rb +4 -4
- data/lib/mocha/integration/test_unit.rb +10 -33
- data/lib/mocha/invocation.rb +12 -16
- data/lib/mocha/minitest.rb +2 -4
- data/lib/mocha/mock.rb +32 -32
- data/lib/mocha/mockery.rb +1 -3
- data/lib/mocha/names.rb +1 -1
- data/lib/mocha/parameter_matchers/base.rb +1 -1
- data/lib/mocha/parameter_matchers/equivalent_uri.rb +1 -2
- data/lib/mocha/parameter_matchers/has_entry.rb +22 -13
- data/lib/mocha/parameter_matchers/has_keys.rb +53 -0
- data/lib/mocha/parameter_matchers/instance_methods.rb +10 -1
- data/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb +64 -0
- data/lib/mocha/parameter_matchers.rb +1 -0
- data/lib/mocha/parameters_matcher.rb +3 -3
- data/lib/mocha/ruby_version.rb +1 -2
- data/lib/mocha/stubbed_method.rb +5 -42
- data/lib/mocha/test_unit.rb +2 -4
- data/lib/mocha/version.rb +1 -1
- data/lib/mocha.rb +0 -8
- data/mocha.gemspec +4 -29
- metadata +10 -87
- data/bin/build-matrix +0 -82
- data/gemfiles/Gemfile.minitest.1.3.0 +0 -7
- data/gemfiles/Gemfile.minitest.1.4.0 +0 -7
- data/gemfiles/Gemfile.minitest.1.4.1 +0 -7
- data/gemfiles/Gemfile.minitest.1.4.2 +0 -7
- data/gemfiles/Gemfile.minitest.2.0.0 +0 -7
- data/gemfiles/Gemfile.minitest.2.0.1 +0 -7
- data/gemfiles/Gemfile.minitest.2.11.0 +0 -7
- data/gemfiles/Gemfile.minitest.2.11.2 +0 -7
- data/gemfiles/Gemfile.minitest.2.3.0 +0 -7
- data/gemfiles/Gemfile.minitest.5.11.3 +0 -7
- data/gemfiles/Gemfile.test-unit.2.0.0 +0 -7
- data/gemfiles/Gemfile.test-unit.2.0.1 +0 -7
- data/gemfiles/Gemfile.test-unit.2.0.3 +0 -7
- data/init.rb +0 -1
- data/lib/mocha/integration/mini_test/nothing.rb +0 -19
- data/lib/mocha/integration/mini_test/version_13.rb +0 -54
- data/lib/mocha/integration/mini_test/version_140.rb +0 -54
- data/lib/mocha/integration/mini_test/version_141.rb +0 -65
- data/lib/mocha/integration/mini_test/version_142_to_172.rb +0 -65
- data/lib/mocha/integration/mini_test/version_200.rb +0 -66
- data/lib/mocha/integration/mini_test/version_201_to_222.rb +0 -66
- data/lib/mocha/integration/mini_test/version_2110_to_2111.rb +0 -70
- data/lib/mocha/integration/mini_test/version_2112_to_320.rb +0 -73
- data/lib/mocha/integration/mini_test/version_230_to_2101.rb +0 -68
- data/lib/mocha/integration/test_unit/gem_version_200.rb +0 -62
- data/lib/mocha/integration/test_unit/gem_version_201_to_202.rb +0 -62
- data/lib/mocha/integration/test_unit/gem_version_203_to_220.rb +0 -62
- data/lib/mocha/integration/test_unit/gem_version_230_to_250.rb +0 -68
- data/lib/mocha/integration/test_unit/nothing.rb +0 -19
- data/lib/mocha/integration/test_unit/ruby_version_185_and_below.rb +0 -61
- data/lib/mocha/integration/test_unit/ruby_version_186_and_above.rb +0 -63
- data/lib/mocha/integration.rb +0 -11
- data/lib/mocha/setup.rb +0 -14
- data/lib/mocha/singleton_class.rb +0 -9
- data/yard-templates/default/layout/html/google_analytics.erb +0 -8
- data/yard-templates/default/layout/html/setup.rb +0 -5
data/lib/mocha/api.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'mocha/ruby_version'
|
1
2
|
require 'mocha/parameter_matchers'
|
2
3
|
require 'mocha/hooks'
|
3
4
|
require 'mocha/mockery'
|
@@ -51,7 +52,6 @@ module Mocha
|
|
51
52
|
#
|
52
53
|
# @overload def mock(name)
|
53
54
|
# @param [String, Symbol] name identifies mock object in error messages.
|
54
|
-
# @note Prior to v1.10.0 when +name+ was a +Symbol+, this method returned an unnamed +Mock+ that expected the method identified by +name+. This was undocumented behaviour and it will be removed in the future, but for the moment it can be reinstated using {Configuration#reinstate_undocumented_behaviour_from_v1_9=}.
|
55
55
|
# @overload def mock(expected_methods_vs_return_values = {})
|
56
56
|
# @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {Mock#expects} were called multiple times.
|
57
57
|
# @overload def mock(name, expected_methods_vs_return_values = {})
|
@@ -66,26 +66,8 @@ module Mocha
|
|
66
66
|
# # an error will be raised unless both Motor#start and Motor#stop have been called
|
67
67
|
# end
|
68
68
|
#
|
69
|
-
def mock(*arguments)
|
70
|
-
if
|
71
|
-
if arguments.first.is_a?(Symbol)
|
72
|
-
method_name = arguments[0]
|
73
|
-
Deprecation.warning(
|
74
|
-
"Explicitly include `#{method_name}` in Hash of expected methods vs return values,",
|
75
|
-
" e.g. `mock(:#{method_name} => nil)`."
|
76
|
-
)
|
77
|
-
if arguments[1]
|
78
|
-
Deprecation.warning(
|
79
|
-
"In this case the 2nd argument for `mock(:##{method_name}, ...)` is ignored,",
|
80
|
-
' but in the future a Hash of expected methods vs return values will be respected.'
|
81
|
-
)
|
82
|
-
end
|
83
|
-
elsif arguments.first.is_a?(String)
|
84
|
-
name = arguments.shift
|
85
|
-
end
|
86
|
-
elsif arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
|
87
|
-
name = arguments.shift
|
88
|
-
end
|
69
|
+
def mock(*arguments)
|
70
|
+
name = arguments.shift.to_s if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
|
89
71
|
expectations = arguments.shift || {}
|
90
72
|
mock = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
|
91
73
|
mock.expects(expectations)
|
@@ -98,11 +80,11 @@ module Mocha
|
|
98
80
|
#
|
99
81
|
# @overload def stub(name)
|
100
82
|
# @param [String, Symbol] name identifies mock object in error messages.
|
101
|
-
# @note Prior to v1.10.0 when +name+ was a +Symbol+, this method returned an unnamed +Mock+ that stubbed the method identified by +name+. This was undocumented behaviour and it will be removed in the future, but for the moment it can be reinstated using {Configuration#reinstate_undocumented_behaviour_from_v1_9=}.
|
102
83
|
# @overload def stub(stubbed_methods_vs_return_values = {})
|
103
84
|
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times.
|
104
85
|
# @overload def stub(name, stubbed_methods_vs_return_values = {})
|
105
86
|
# @param [String, Symbol] name identifies mock object in error messages.
|
87
|
+
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times.
|
106
88
|
#
|
107
89
|
# @example Using stubbed_methods_vs_return_values Hash to setup stubbed methods.
|
108
90
|
# def test_motor_starts_and_stops
|
@@ -111,33 +93,13 @@ module Mocha
|
|
111
93
|
# assert motor.stop
|
112
94
|
# # an error will not be raised even if either Motor#start or Motor#stop has not been called
|
113
95
|
# end
|
114
|
-
# rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
115
96
|
def stub(*arguments)
|
116
|
-
if
|
117
|
-
if arguments.first.is_a?(Symbol)
|
118
|
-
method_name = arguments[0]
|
119
|
-
Deprecation.warning(
|
120
|
-
"Explicitly include `#{method_name}` in Hash of stubbed methods vs return values,",
|
121
|
-
" e.g. `stub(:#{method_name} => nil)`."
|
122
|
-
)
|
123
|
-
if arguments[1]
|
124
|
-
Deprecation.warning(
|
125
|
-
"In this case the 2nd argument for `stub(:##{method_name}, ...)` is ignored,",
|
126
|
-
' but in the future a Hash of stubbed methods vs return values will be respected.'
|
127
|
-
)
|
128
|
-
end
|
129
|
-
elsif arguments.first.is_a?(String)
|
130
|
-
name = arguments.shift
|
131
|
-
end
|
132
|
-
elsif arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
|
133
|
-
name = arguments.shift
|
134
|
-
end
|
97
|
+
name = arguments.shift.to_s if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
|
135
98
|
expectations = arguments.shift || {}
|
136
99
|
stub = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
|
137
100
|
stub.stubs(expectations)
|
138
101
|
stub
|
139
102
|
end
|
140
|
-
# rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
141
103
|
|
142
104
|
# Builds a mock object that accepts calls to any method. By default it will return +nil+ for any method call.
|
143
105
|
#
|
@@ -145,7 +107,6 @@ module Mocha
|
|
145
107
|
#
|
146
108
|
# @overload def stub_everything(name)
|
147
109
|
# @param [String, Symbol] name identifies mock object in error messages.
|
148
|
-
# @note Prior to v1.10.0 when +name+ was a +Symbol+, this method returned an unnamed +Mock+ that stubbed the method identified by +name+. This was undocumented behaviour and it will be removed in the future, but for the moment it can be reinstated using {Configuration#reinstate_undocumented_behaviour_from_v1_9=}.
|
149
110
|
# @overload def stub_everything(stubbed_methods_vs_return_values = {})
|
150
111
|
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times.
|
151
112
|
# @overload def stub_everything(name, stubbed_methods_vs_return_values = {})
|
@@ -159,34 +120,14 @@ module Mocha
|
|
159
120
|
# assert_nil motor.irrelevant_method_2 # => no error raised
|
160
121
|
# assert motor.stop
|
161
122
|
# end
|
162
|
-
# rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
163
123
|
def stub_everything(*arguments)
|
164
|
-
if
|
165
|
-
if arguments.first.is_a?(Symbol)
|
166
|
-
method_name = arguments[0]
|
167
|
-
Deprecation.warning(
|
168
|
-
"Explicitly include `#{method_name}` in Hash of stubbed methods vs return values,",
|
169
|
-
" e.g. `stub_everything(:#{method_name} => nil)`."
|
170
|
-
)
|
171
|
-
if arguments[1]
|
172
|
-
Deprecation.warning(
|
173
|
-
"In this case the 2nd argument for `stub_everything(:##{method_name}, ...)` is ignored,",
|
174
|
-
' but in the future a Hash of stubbed methods vs return values will be respected.'
|
175
|
-
)
|
176
|
-
end
|
177
|
-
elsif arguments.first.is_a?(String)
|
178
|
-
name = arguments.shift
|
179
|
-
end
|
180
|
-
elsif arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
|
181
|
-
name = arguments.shift
|
182
|
-
end
|
124
|
+
name = arguments.shift if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
|
183
125
|
expectations = arguments.shift || {}
|
184
126
|
stub = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
|
185
127
|
stub.stub_everything
|
186
128
|
stub.stubs(expectations)
|
187
129
|
stub
|
188
130
|
end
|
189
|
-
# rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
190
131
|
|
191
132
|
# Builds a new sequence which can be used to constrain the order in which expectations can occur.
|
192
133
|
#
|
@@ -199,11 +140,22 @@ module Mocha
|
|
199
140
|
# @example Ensure methods on egg are invoked in correct order.
|
200
141
|
# breakfast = sequence('breakfast')
|
201
142
|
#
|
202
|
-
# egg = mock('egg')
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
#
|
143
|
+
# egg = mock('egg')
|
144
|
+
# egg.expects(:crack).in_sequence(breakfast)
|
145
|
+
# egg.expects(:fry).in_sequence(breakfast)
|
146
|
+
# egg.expects(:eat).in_sequence(breakfast)
|
147
|
+
#
|
148
|
+
# @example Ensure methods across multiple objects are invoked in correct order.
|
149
|
+
# sequence = sequence(:task_order)
|
150
|
+
#
|
151
|
+
# task_one = mock("task_one")
|
152
|
+
# task_two = mock("task_two")
|
153
|
+
#
|
154
|
+
# task_one.expects(:execute).in_sequence(sequence)
|
155
|
+
# task_two.expects(:execute).in_sequence(sequence)
|
156
|
+
#
|
157
|
+
# task_one.execute
|
158
|
+
# task_two.execute
|
207
159
|
def sequence(name)
|
208
160
|
Sequence.new(name)
|
209
161
|
end
|
@@ -226,14 +178,13 @@ module Mocha
|
|
226
178
|
# @example Constrain expected invocations to occur in particular states.
|
227
179
|
# power = states('power').starts_as('off')
|
228
180
|
#
|
229
|
-
# radio = mock('radio')
|
230
|
-
#
|
231
|
-
#
|
232
|
-
#
|
233
|
-
#
|
234
|
-
#
|
235
|
-
#
|
236
|
-
# end
|
181
|
+
# radio = mock('radio')
|
182
|
+
# radio.expects(:switch_on).then(power.is('on'))
|
183
|
+
# radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
|
184
|
+
# radio.expects(:adjust_volume).with(+5).when(power.is('on'))
|
185
|
+
# radio.expects(:select_channel).with('BBC World Service').when(power.is('on'))
|
186
|
+
# radio.expects(:adjust_volume).with(-5).when(power.is('on'))
|
187
|
+
# radio.expects(:switch_off).then(power.is('off'))
|
237
188
|
def states(name)
|
238
189
|
Mockery.instance.new_state_machine(name)
|
239
190
|
end
|
@@ -3,11 +3,11 @@ module Mocha
|
|
3
3
|
LIB_DIRECTORY = File.expand_path(File.join(File.dirname(__FILE__), '..')) + File::SEPARATOR
|
4
4
|
|
5
5
|
def initialize(lib_directory = LIB_DIRECTORY)
|
6
|
-
@
|
6
|
+
@lib_directory = lib_directory
|
7
7
|
end
|
8
8
|
|
9
9
|
def filtered(backtrace)
|
10
|
-
backtrace.reject { |location|
|
10
|
+
backtrace.reject { |location| File.expand_path(location).start_with?(@lib_directory) }
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
data/lib/mocha/class_methods.rb
CHANGED
@@ -26,8 +26,8 @@ module Mocha
|
|
26
26
|
@stubba_object
|
27
27
|
end
|
28
28
|
|
29
|
-
def respond_to?(
|
30
|
-
@stubba_object.allocate.respond_to?(
|
29
|
+
def respond_to?(symbol, include_all = false)
|
30
|
+
@stubba_object.allocate.respond_to?(symbol.to_sym, include_all)
|
31
31
|
end
|
32
32
|
|
33
33
|
attr_reader :stubba_object
|
data/lib/mocha/configuration.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'mocha/ruby_version'
|
2
|
+
|
1
3
|
module Mocha
|
2
4
|
# Allows setting of configuration options. See {Configuration} for the available options.
|
3
5
|
#
|
@@ -37,13 +39,13 @@ module Mocha
|
|
37
39
|
class Configuration
|
38
40
|
# @private
|
39
41
|
DEFAULTS = {
|
40
|
-
:
|
41
|
-
:
|
42
|
-
:
|
43
|
-
:
|
44
|
-
:
|
45
|
-
:
|
46
|
-
:
|
42
|
+
stubbing_method_unnecessarily: :allow,
|
43
|
+
stubbing_method_on_non_mock_object: :allow,
|
44
|
+
stubbing_non_existent_method: :allow,
|
45
|
+
stubbing_non_public_method: :allow,
|
46
|
+
stubbing_method_on_nil: :prevent,
|
47
|
+
display_matching_invocations_on_failure: false,
|
48
|
+
strict_keyword_argument_matching: false
|
47
49
|
}.freeze
|
48
50
|
|
49
51
|
attr_reader :options
|
@@ -248,134 +250,54 @@ module Mocha
|
|
248
250
|
@options[:display_matching_invocations_on_failure]
|
249
251
|
end
|
250
252
|
|
251
|
-
#
|
252
|
-
#
|
253
|
-
# Previously when {API#mock}, {API#stub}, or {API#stub_everything} were called with the first argument being a symbol, they built an *unnamed* mock object *and* expected or stubbed the method identified by the symbol argument; subsequent arguments were ignored.
|
254
|
-
# Now these methods build a *named* mock with the name specified by the symbol argument; *no* methods are expected or stubbed and subsequent arguments *are* taken into account.
|
253
|
+
# Perform strict keyword argument comparison. Only supported in Ruby >= v2.7.
|
255
254
|
#
|
256
|
-
#
|
257
|
-
# Now a +LocalJumpError+ is raised.
|
255
|
+
# When this option is set to +false+ a positional +Hash+ and a set of keyword arguments are treated the same during comparison, which can lead to misleading passing tests in Ruby >= v3.0 (see examples below). However, a deprecation warning will be displayed if a positional +Hash+ matches a set of keyword arguments or vice versa. This is because {#strict_keyword_argument_matching=} will default to +true+ in the future.
|
258
256
|
#
|
259
|
-
#
|
257
|
+
# For more details on keyword arguments in Ruby v3, refer to {https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0 this article}.
|
260
258
|
#
|
261
|
-
#
|
259
|
+
# Note that +Hash+-related matchers such as {ParameterMatchers#has_value} or {ParameterMatchers#has_key} will still treat a positional +Hash+ and a set of keyword arguments the same, so misleading passing tests are still possible when they are used.
|
262
260
|
#
|
263
|
-
#
|
264
|
-
# Mocha.configure do |c|
|
265
|
-
# c.reinstate_undocumented_behaviour_from_v1_9 = true
|
266
|
-
# end
|
261
|
+
# This configuration option is +false+ by default to enable gradual adoption, but will be +true+ by default in the future.
|
267
262
|
#
|
268
|
-
#
|
269
|
-
# foo.inspect # => #<Mock>
|
263
|
+
# @param [Boolean] value +true+ to enable strict keyword argument matching; +false+ by default.
|
270
264
|
#
|
271
|
-
#
|
272
|
-
# unsatisfied expectations:
|
273
|
-
# - expected exactly once, invoked never: #<Mock>.foo
|
265
|
+
# @example Loose keyword argument matching (default)
|
274
266
|
#
|
275
|
-
#
|
276
|
-
#
|
277
|
-
# c.reinstate_undocumented_behaviour_from_v1_9 = true
|
267
|
+
# class Example
|
268
|
+
# def foo(a, bar:); end
|
278
269
|
# end
|
279
270
|
#
|
280
|
-
#
|
281
|
-
# foo.
|
282
|
-
# foo
|
271
|
+
# example = Example.new
|
272
|
+
# example.expects(:foo).with('a', bar: 'b')
|
273
|
+
# example.foo('a', { bar: 'b' })
|
274
|
+
# # This passes the test, but would result in an ArgumentError in practice
|
283
275
|
#
|
284
|
-
# @example
|
285
|
-
# foo = mock('foo')
|
286
|
-
# foo.stubs(:my_method).yields(1, 2)
|
287
|
-
# foo.my_method # => raises LocalJumpError when no block is supplied
|
276
|
+
# @example Strict keyword argument matching
|
288
277
|
#
|
289
278
|
# Mocha.configure do |c|
|
290
|
-
# c.
|
279
|
+
# c.strict_keyword_argument_matching = true
|
291
280
|
# end
|
292
281
|
#
|
293
|
-
#
|
294
|
-
#
|
295
|
-
#
|
282
|
+
# class Example
|
283
|
+
# def foo(a, bar:); end
|
284
|
+
# end
|
296
285
|
#
|
297
|
-
|
298
|
-
|
286
|
+
# example = Example.new
|
287
|
+
# example.expects(:foo).with('a', bar: 'b')
|
288
|
+
# example.foo('a', { bar: 'b' })
|
289
|
+
# # This now fails as expected
|
290
|
+
def strict_keyword_argument_matching=(value)
|
291
|
+
raise 'Strict keyword argument matching requires Ruby 2.7 and above.' unless Mocha::RUBY_V27_PLUS
|
292
|
+
@options[:strict_keyword_argument_matching] = value
|
299
293
|
end
|
300
294
|
|
301
295
|
# @private
|
302
|
-
def
|
303
|
-
@options[:
|
296
|
+
def strict_keyword_argument_matching?
|
297
|
+
@options[:strict_keyword_argument_matching]
|
304
298
|
end
|
305
299
|
|
306
300
|
class << self
|
307
|
-
# Allow the specified +action+.
|
308
|
-
#
|
309
|
-
# @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
|
310
|
-
# @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
|
311
|
-
# @deprecated If a block is supplied, call {.override} with a +Hash+ containing an entry with the +action+ as the key and +:allow+ as the value. If no block is supplied, call the appropriate +action+ writer method with +value+ set to +:allow+ via {Mocha.configure}. The writer method will be the one of the following corresponding to the +action+:
|
312
|
-
# * {#stubbing_method_unnecessarily=}
|
313
|
-
# * {#stubbing_method_on_non_mock_object=}
|
314
|
-
# * {#stubbing_non_existent_method=}
|
315
|
-
# * {#stubbing_non_public_method=}
|
316
|
-
# * {#stubbing_method_on_nil=}
|
317
|
-
def allow(action, &block)
|
318
|
-
if block_given?
|
319
|
-
Deprecation.warning("Use Mocha::Configuration.override(#{action}: :allow) with the same block")
|
320
|
-
else
|
321
|
-
Deprecation.warning("Use Mocha.configure { |c| c.#{action} = :allow }")
|
322
|
-
end
|
323
|
-
change_config action, :allow, &block
|
324
|
-
end
|
325
|
-
|
326
|
-
# @private
|
327
|
-
def allow?(action)
|
328
|
-
configuration.allow?(action)
|
329
|
-
end
|
330
|
-
|
331
|
-
# Warn if the specified +action+ is attempted.
|
332
|
-
#
|
333
|
-
# @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
|
334
|
-
# @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
|
335
|
-
# @deprecated If a block is supplied, call {.override} with a +Hash+ containing an entry with the +action+ as the key and +:warn+ as the value. If no block is supplied, call the appropriate +action+ writer method with +value+ set to +:warn+ via {Mocha.configure}. The writer method will be the one of the following corresponding to the +action+:
|
336
|
-
# * {#stubbing_method_unnecessarily=}
|
337
|
-
# * {#stubbing_method_on_non_mock_object=}
|
338
|
-
# * {#stubbing_non_existent_method=}
|
339
|
-
# * {#stubbing_non_public_method=}
|
340
|
-
# * {#stubbing_method_on_nil=}
|
341
|
-
def warn_when(action, &block)
|
342
|
-
if block_given?
|
343
|
-
Deprecation.warning("Use Mocha::Configuration.override(#{action}: :warn) with the same block")
|
344
|
-
else
|
345
|
-
Deprecation.warning("Use Mocha.configure { |c| c.#{action} = :warn }")
|
346
|
-
end
|
347
|
-
change_config action, :warn, &block
|
348
|
-
end
|
349
|
-
|
350
|
-
# @private
|
351
|
-
def warn_when?(action)
|
352
|
-
configuration.warn_when?(action)
|
353
|
-
end
|
354
|
-
|
355
|
-
# Raise a {StubbingError} if the specified +action+ is attempted.
|
356
|
-
#
|
357
|
-
# @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
|
358
|
-
# @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
|
359
|
-
# @deprecated If a block is supplied, call {.override} with a +Hash+ containing an entry with the +action+ as the key and +:prevent+ as the value. If no block is supplied, call the appropriate +action+ writer method with +value+ set to +:prevent+ via {Mocha.configure}. The writer method will be the one of the following corresponding to the +action+:
|
360
|
-
# * {#stubbing_method_unnecessarily=}
|
361
|
-
# * {#stubbing_method_on_non_mock_object=}
|
362
|
-
# * {#stubbing_non_existent_method=}
|
363
|
-
# * {#stubbing_non_public_method=}
|
364
|
-
# * {#stubbing_method_on_nil=}
|
365
|
-
def prevent(action, &block)
|
366
|
-
if block_given?
|
367
|
-
Deprecation.warning("Use Mocha::Configuration.override(#{action}: :prevent) with the same block")
|
368
|
-
else
|
369
|
-
Deprecation.warning("Use Mocha.configure { |c| c.#{action} = :prevent }")
|
370
|
-
end
|
371
|
-
change_config action, :prevent, &block
|
372
|
-
end
|
373
|
-
|
374
|
-
# @private
|
375
|
-
def prevent?(action)
|
376
|
-
configuration.prevent?(action)
|
377
|
-
end
|
378
|
-
|
379
301
|
# @private
|
380
302
|
def reset_configuration
|
381
303
|
@configuration = nil
|
data/lib/mocha/debug.rb
CHANGED
@@ -1,12 +1,9 @@
|
|
1
1
|
module Mocha
|
2
2
|
module Debug
|
3
|
-
OPTIONS = (ENV['MOCHA_OPTIONS'] || '').split(',').
|
4
|
-
hash[key] = true
|
5
|
-
hash
|
6
|
-
end.freeze
|
3
|
+
OPTIONS = (ENV['MOCHA_OPTIONS'] || '').split(',').freeze
|
7
4
|
|
8
5
|
def self.puts(message)
|
9
|
-
warn(message) if OPTIONS
|
6
|
+
warn(message) if OPTIONS.include?('debug')
|
10
7
|
end
|
11
8
|
end
|
12
9
|
end
|
data/lib/mocha/expectation.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'ruby2_keywords'
|
1
2
|
require 'mocha/method_matcher'
|
2
3
|
require 'mocha/parameters_matcher'
|
3
4
|
require 'mocha/expectation_error'
|
@@ -11,6 +12,7 @@ require 'mocha/change_state_side_effect'
|
|
11
12
|
require 'mocha/cardinality'
|
12
13
|
require 'mocha/configuration'
|
13
14
|
require 'mocha/block_matcher'
|
15
|
+
require 'mocha/backtrace_filter'
|
14
16
|
|
15
17
|
module Mocha
|
16
18
|
# Methods on expectations returned from {Mock#expects}, {Mock#stubs}, {ObjectMethods#expects} and {ObjectMethods#stubs}.
|
@@ -187,17 +189,26 @@ module Mocha
|
|
187
189
|
at_most(1)
|
188
190
|
end
|
189
191
|
|
190
|
-
# Modifies expectation so that the expected method must be called with +
|
192
|
+
# Modifies expectation so that the expected method must be called with +expected_parameters_or_matchers+.
|
191
193
|
#
|
192
|
-
# May be used with parameter matchers
|
194
|
+
# May be used with Ruby literals or variables for exact matching or with parameter matchers for less-specific matching, e.g. {ParameterMatchers#includes}, {ParameterMatchers#has_key}, etc. See {ParameterMatchers} for a list of all available parameter matchers.
|
193
195
|
#
|
194
|
-
#
|
196
|
+
# Positional arguments were separated from keyword arguments in Ruby v3 (see {https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0 this article}). In relation to this a new configuration option ({Configuration#strict_keyword_argument_matching=}) is available in Ruby >= 2.7.
|
197
|
+
#
|
198
|
+
# When {Configuration#strict_keyword_argument_matching=} is set to +false+ (which is currently the default), a positional +Hash+ and a set of keyword arguments passed to {#with} are treated the same for the purposes of parameter matching. However, a deprecation warning will be displayed if a positional +Hash+ matches a set of keyword arguments or vice versa. This is because {Configuration#strict_keyword_argument_matching=} will default to +true+ in the future.
|
199
|
+
#
|
200
|
+
# When {Configuration#strict_keyword_argument_matching=} is set to +true+, an actual positional +Hash+ will not match an expected set of keyword arguments; and vice versa, an actual set of keyword arguments will not match an expected positional +Hash+, i.e. the parameter matching is stricter.
|
201
|
+
#
|
202
|
+
# @see ParameterMatchers
|
203
|
+
# @see Configuration#strict_keyword_argument_matching=
|
204
|
+
#
|
205
|
+
# @param [*Array<Object,ParameterMatchers::Base>] expected_parameters_or_matchers expected parameter values or parameter matchers.
|
195
206
|
# @yield optional block specifying custom matching.
|
196
|
-
# @yieldparam [*Array] actual_parameters parameters with which expected method was invoked.
|
207
|
+
# @yieldparam [*Array<Object>] actual_parameters parameters with which expected method was invoked.
|
197
208
|
# @yieldreturn [Boolean] +true+ if +actual_parameters+ are acceptable.
|
198
209
|
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
199
210
|
#
|
200
|
-
# @example Expected method must be called with
|
211
|
+
# @example Expected method must be called with exact parameter values.
|
201
212
|
# object = mock()
|
202
213
|
# object.expects(:expected_method).with(:param1, :param2)
|
203
214
|
# object.expected_method(:param1, :param2)
|
@@ -208,6 +219,43 @@ module Mocha
|
|
208
219
|
# object.expected_method(:param3)
|
209
220
|
# # => verify fails
|
210
221
|
#
|
222
|
+
# @example Expected method must be called with parameters matching parameter matchers.
|
223
|
+
# object = mock()
|
224
|
+
# object.expects(:expected_method).with(includes('string2'), anything)
|
225
|
+
# object.expected_method(['string1', 'string2'], 'any-old-value')
|
226
|
+
# # => verify succeeds
|
227
|
+
#
|
228
|
+
# object = mock()
|
229
|
+
# object.expects(:expected_method).with(includes('string2'), anything)
|
230
|
+
# object.expected_method(['string1'], 'any-old-value')
|
231
|
+
# # => verify fails
|
232
|
+
#
|
233
|
+
# @example Loose keyword argument matching (default)
|
234
|
+
#
|
235
|
+
# class Example
|
236
|
+
# def foo(a, bar:); end
|
237
|
+
# end
|
238
|
+
#
|
239
|
+
# example = Example.new
|
240
|
+
# example.expects(:foo).with('a', bar: 'b')
|
241
|
+
# example.foo('a', { bar: 'b' })
|
242
|
+
# # This passes the test, but would result in an ArgumentError in practice
|
243
|
+
#
|
244
|
+
# @example Strict keyword argument matching
|
245
|
+
#
|
246
|
+
# Mocha.configure do |c|
|
247
|
+
# c.strict_keyword_argument_matching = true
|
248
|
+
# end
|
249
|
+
#
|
250
|
+
# class Example
|
251
|
+
# def foo(a, bar:); end
|
252
|
+
# end
|
253
|
+
#
|
254
|
+
# example = Example.new
|
255
|
+
# example.expects(:foo).with('a', bar: 'b')
|
256
|
+
# example.foo('a', { bar: 'b' })
|
257
|
+
# # This now fails as expected
|
258
|
+
#
|
211
259
|
# @example Expected method must be called with a value divisible by 4.
|
212
260
|
# object = mock()
|
213
261
|
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
|
@@ -218,10 +266,11 @@ module Mocha
|
|
218
266
|
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
|
219
267
|
# object.expected_method(17)
|
220
268
|
# # => verify fails
|
221
|
-
def with(*
|
222
|
-
@parameters_matcher = ParametersMatcher.new(
|
269
|
+
def with(*expected_parameters_or_matchers, &matching_block)
|
270
|
+
@parameters_matcher = ParametersMatcher.new(expected_parameters_or_matchers, self, &matching_block)
|
223
271
|
self
|
224
272
|
end
|
273
|
+
ruby2_keywords(:with)
|
225
274
|
|
226
275
|
# Modifies expectation so that the expected method must be called with a block.
|
227
276
|
#
|
@@ -625,7 +674,7 @@ module Mocha
|
|
625
674
|
def inspect
|
626
675
|
address = __id__ * 2
|
627
676
|
address += 0x100000000 if address < 0
|
628
|
-
"#<Expectation:0x#{format('
|
677
|
+
"#<Expectation:0x#{format('%<address>x', address: address)} #{mocha_inspect} >"
|
629
678
|
end
|
630
679
|
|
631
680
|
# @private
|
@@ -644,5 +693,11 @@ module Mocha
|
|
644
693
|
signature << " #{@block_matcher.mocha_inspect}" if @block_matcher.mocha_inspect
|
645
694
|
signature
|
646
695
|
end
|
696
|
+
|
697
|
+
# @private
|
698
|
+
def definition_location
|
699
|
+
filter = BacktraceFilter.new
|
700
|
+
filter.filtered(backtrace)[0]
|
701
|
+
end
|
647
702
|
end
|
648
703
|
end
|
data/lib/mocha/inspect.rb
CHANGED
@@ -6,19 +6,21 @@ module Mocha
|
|
6
6
|
def mocha_inspect
|
7
7
|
address = __id__ * 2
|
8
8
|
address += 0x100000000 if address < 0
|
9
|
-
inspect =~ /#</ ? "#<#{self.class}:0x#{Kernel.format('
|
9
|
+
inspect =~ /#</ ? "#<#{self.class}:0x#{Kernel.format('%<address>x', address: address)}>" : inspect
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
13
|
module ArrayMethods
|
14
|
-
def mocha_inspect
|
15
|
-
|
14
|
+
def mocha_inspect(wrapped = true)
|
15
|
+
unwrapped = collect(&:mocha_inspect).join(', ')
|
16
|
+
wrapped ? "[#{unwrapped}]" : unwrapped
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
19
20
|
module HashMethods
|
20
21
|
def mocha_inspect
|
21
|
-
|
22
|
+
unwrapped = collect { |key, value| "#{key.mocha_inspect} => #{value.mocha_inspect}" }.join(', ')
|
23
|
+
Hash.ruby2_keywords_hash?(self) ? unwrapped : "{#{unwrapped}}"
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
@@ -1,54 +1,26 @@
|
|
1
1
|
require 'mocha/debug'
|
2
|
-
|
3
2
|
require 'mocha/detection/mini_test'
|
4
|
-
|
5
|
-
require 'mocha/integration/mini_test/nothing'
|
6
|
-
require 'mocha/integration/mini_test/version_13'
|
7
|
-
require 'mocha/integration/mini_test/version_140'
|
8
|
-
require 'mocha/integration/mini_test/version_141'
|
9
|
-
require 'mocha/integration/mini_test/version_142_to_172'
|
10
|
-
require 'mocha/integration/mini_test/version_200'
|
11
|
-
require 'mocha/integration/mini_test/version_201_to_222'
|
12
|
-
require 'mocha/integration/mini_test/version_230_to_2101'
|
13
|
-
require 'mocha/integration/mini_test/version_2110_to_2111'
|
14
|
-
require 'mocha/integration/mini_test/version_2112_to_320'
|
15
3
|
require 'mocha/integration/mini_test/adapter'
|
16
4
|
|
17
|
-
require 'mocha/deprecation'
|
18
|
-
|
19
5
|
module Mocha
|
20
6
|
module Integration
|
21
7
|
module MiniTest
|
22
8
|
def self.activate
|
23
|
-
|
24
|
-
|
9
|
+
target = Detection::MiniTest.testcase
|
10
|
+
return false unless target
|
25
11
|
|
12
|
+
mini_test_version = Gem::Version.new(Detection::MiniTest.version)
|
26
13
|
Debug.puts "Detected MiniTest version: #{mini_test_version}"
|
27
14
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
MiniTest::Version2110To2111,
|
32
|
-
MiniTest::Version230To2101,
|
33
|
-
MiniTest::Version201To222,
|
34
|
-
MiniTest::Version200,
|
35
|
-
MiniTest::Version142To172,
|
36
|
-
MiniTest::Version141,
|
37
|
-
MiniTest::Version140,
|
38
|
-
MiniTest::Version13,
|
39
|
-
MiniTest::Nothing
|
40
|
-
].detect { |m| m.applicable_to?(mini_test_version) }
|
15
|
+
unless MiniTest::Adapter.applicable_to?(mini_test_version)
|
16
|
+
raise 'Versions of minitest earlier than v3.3.0 are not supported.'
|
17
|
+
end
|
41
18
|
|
42
|
-
target
|
43
|
-
|
44
|
-
|
45
|
-
Deprecation.warning(
|
46
|
-
'Versions of minitest earlier than v3.3.0 will not be supported in future versions of Mocha.'
|
47
|
-
)
|
48
|
-
end
|
49
|
-
Debug.puts "Applying #{integration_module.description}"
|
50
|
-
target.send(:include, integration_module)
|
19
|
+
unless target < MiniTest::Adapter
|
20
|
+
Debug.puts "Applying #{MiniTest::Adapter.description}"
|
21
|
+
target.send(:include, MiniTest::Adapter)
|
51
22
|
end
|
23
|
+
|
52
24
|
true
|
53
25
|
end
|
54
26
|
end
|