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
@@ -12,7 +12,7 @@ module Mocha
|
|
12
12
|
include Mocha::API
|
13
13
|
|
14
14
|
# @private
|
15
|
-
def self.applicable_to?(test_unit_version
|
15
|
+
def self.applicable_to?(test_unit_version)
|
16
16
|
Gem::Requirement.new('>= 2.5.1').satisfied_by?(test_unit_version)
|
17
17
|
end
|
18
18
|
|
@@ -23,16 +23,16 @@ module Mocha
|
|
23
23
|
|
24
24
|
# @private
|
25
25
|
def self.included(mod)
|
26
|
-
mod.setup :mocha_setup, :
|
26
|
+
mod.setup :mocha_setup, before: :prepend
|
27
27
|
|
28
28
|
mod.exception_handler(:handle_mocha_expectation_error)
|
29
29
|
|
30
|
-
mod.cleanup :
|
30
|
+
mod.cleanup after: :append do
|
31
31
|
assertion_counter = Integration::AssertionCounter.new(self)
|
32
32
|
mocha_verify(assertion_counter)
|
33
33
|
end
|
34
34
|
|
35
|
-
mod.teardown :mocha_teardown, :
|
35
|
+
mod.teardown :mocha_teardown, after: :append
|
36
36
|
end
|
37
37
|
|
38
38
|
private
|
@@ -1,49 +1,26 @@
|
|
1
1
|
require 'mocha/debug'
|
2
|
-
|
3
2
|
require 'mocha/detection/test_unit'
|
4
|
-
|
5
|
-
require 'mocha/integration/test_unit/nothing'
|
6
|
-
require 'mocha/integration/test_unit/ruby_version_185_and_below'
|
7
|
-
require 'mocha/integration/test_unit/ruby_version_186_and_above'
|
8
|
-
require 'mocha/integration/test_unit/gem_version_200'
|
9
|
-
require 'mocha/integration/test_unit/gem_version_201_to_202'
|
10
|
-
require 'mocha/integration/test_unit/gem_version_203_to_220'
|
11
|
-
require 'mocha/integration/test_unit/gem_version_230_to_250'
|
12
3
|
require 'mocha/integration/test_unit/adapter'
|
13
4
|
|
14
|
-
require 'mocha/deprecation'
|
15
|
-
|
16
5
|
module Mocha
|
17
6
|
module Integration
|
18
7
|
module TestUnit
|
19
8
|
def self.activate
|
20
|
-
|
21
|
-
|
22
|
-
ruby_version = Gem::Version.new(RUBY_VERSION.dup)
|
9
|
+
target = Detection::TestUnit.testcase
|
10
|
+
return false unless target
|
23
11
|
|
24
|
-
|
12
|
+
test_unit_version = Gem::Version.new(Detection::TestUnit.version)
|
25
13
|
Debug.puts "Detected Test::Unit version: #{test_unit_version}"
|
26
14
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
TestUnit::GemVersion203To220,
|
31
|
-
TestUnit::GemVersion201To202,
|
32
|
-
TestUnit::GemVersion200,
|
33
|
-
TestUnit::RubyVersion186AndAbove,
|
34
|
-
TestUnit::RubyVersion185AndBelow,
|
35
|
-
TestUnit::Nothing
|
36
|
-
].detect { |m| m.applicable_to?(test_unit_version, ruby_version) }
|
15
|
+
unless TestUnit::Adapter.applicable_to?(test_unit_version)
|
16
|
+
raise 'Versions of test-unit earlier than v2.5.1 are not supported.'
|
17
|
+
end
|
37
18
|
|
38
|
-
unless
|
39
|
-
|
40
|
-
|
41
|
-
'Versions of test-unit earlier than v2.5.1 will not be supported in future versions of Mocha.'
|
42
|
-
)
|
43
|
-
end
|
44
|
-
Debug.puts "Applying #{integration_module.description}"
|
45
|
-
::Test::Unit::TestCase.send(:include, integration_module)
|
19
|
+
unless target < TestUnit::Adapter
|
20
|
+
Debug.puts "Applying #{TestUnit::Adapter.description}"
|
21
|
+
target.send(:include, TestUnit::Adapter)
|
46
22
|
end
|
23
|
+
|
47
24
|
true
|
48
25
|
end
|
49
26
|
end
|
data/lib/mocha/invocation.rb
CHANGED
@@ -3,14 +3,12 @@ require 'mocha/raised_exception'
|
|
3
3
|
require 'mocha/return_values'
|
4
4
|
require 'mocha/thrown_object'
|
5
5
|
require 'mocha/yield_parameters'
|
6
|
-
require 'mocha/configuration'
|
7
|
-
require 'mocha/deprecation'
|
8
6
|
|
9
7
|
module Mocha
|
10
8
|
class Invocation
|
11
9
|
attr_reader :method_name, :block
|
12
10
|
|
13
|
-
def initialize(mock, method_name,
|
11
|
+
def initialize(mock, method_name, arguments = [], block = nil)
|
14
12
|
@mock = mock
|
15
13
|
@method_name = method_name
|
16
14
|
@arguments = arguments
|
@@ -22,18 +20,8 @@ module Mocha
|
|
22
20
|
def call(yield_parameters = YieldParameters.new, return_values = ReturnValues.new)
|
23
21
|
yield_parameters.next_invocation.each do |yield_args|
|
24
22
|
@yields << ParametersMatcher.new(yield_args)
|
25
|
-
|
26
|
-
|
27
|
-
else
|
28
|
-
raise LocalJumpError unless Mocha.configuration.reinstate_undocumented_behaviour_from_v1_9?
|
29
|
-
yield_args_description = ParametersMatcher.new(yield_args).mocha_inspect
|
30
|
-
Deprecation.warning(
|
31
|
-
"Stubbed method was instructed to yield #{yield_args_description}, but no block was given by invocation: #{call_description}.",
|
32
|
-
' This will raise a LocalJumpError in the future.',
|
33
|
-
' Use Expectation#with_block_given to constrain this expectation to match invocations supplying a block.',
|
34
|
-
' And, if necessary, add another expectation to match invocations not supplying a block.'
|
35
|
-
)
|
36
|
-
end
|
23
|
+
raise LocalJumpError unless @block
|
24
|
+
@block.call(*yield_args)
|
37
25
|
end
|
38
26
|
return_values.next(self)
|
39
27
|
end
|
@@ -55,7 +43,7 @@ module Mocha
|
|
55
43
|
end
|
56
44
|
|
57
45
|
def call_description
|
58
|
-
description = "#{@mock.mocha_inspect}.#{@method_name}#{
|
46
|
+
description = "#{@mock.mocha_inspect}.#{@method_name}#{argument_description}"
|
59
47
|
description << ' { ... }' unless @block.nil?
|
60
48
|
description
|
61
49
|
end
|
@@ -73,5 +61,13 @@ module Mocha
|
|
73
61
|
def full_description
|
74
62
|
"\n - #{call_description} #{result_description}"
|
75
63
|
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def argument_description
|
68
|
+
signature = arguments.mocha_inspect
|
69
|
+
signature = signature.gsub(/^\[|\]$/, '')
|
70
|
+
"(#{signature})"
|
71
|
+
end
|
76
72
|
end
|
77
73
|
end
|
data/lib/mocha/minitest.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
+
require 'mocha/ruby_version'
|
1
2
|
require 'mocha/integration/mini_test'
|
2
|
-
require 'mocha/deprecation'
|
3
3
|
|
4
4
|
unless Mocha::Integration::MiniTest.activate
|
5
|
-
|
6
|
-
"MiniTest must be loaded *before* `require 'mocha/minitest'`."
|
7
|
-
)
|
5
|
+
raise "MiniTest must be loaded *before* `require 'mocha/minitest'`."
|
8
6
|
end
|
data/lib/mocha/mock.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'ruby2_keywords'
|
2
2
|
require 'mocha/expectation'
|
3
3
|
require 'mocha/expectation_list'
|
4
4
|
require 'mocha/invocation'
|
@@ -8,7 +8,6 @@ require 'mocha/method_matcher'
|
|
8
8
|
require 'mocha/parameters_matcher'
|
9
9
|
require 'mocha/argument_iterator'
|
10
10
|
require 'mocha/expectation_error_factory'
|
11
|
-
require 'mocha/ruby_version'
|
12
11
|
|
13
12
|
module Mocha
|
14
13
|
# Traditional mock object.
|
@@ -109,6 +108,7 @@ module Mocha
|
|
109
108
|
# object.expects(:expected_method_one).returns(:result_one)
|
110
109
|
# object.expects(:expected_method_two).returns(:result_two)
|
111
110
|
def expects(method_name_or_hash, backtrace = nil)
|
111
|
+
expectation = nil
|
112
112
|
iterator = ArgumentIterator.new(method_name_or_hash)
|
113
113
|
iterator.each do |*args|
|
114
114
|
method_name = args.shift
|
@@ -117,6 +117,7 @@ module Mocha
|
|
117
117
|
expectation.returns(args.shift) unless args.empty?
|
118
118
|
@expectations.add(expectation)
|
119
119
|
end
|
120
|
+
expectation
|
120
121
|
end
|
121
122
|
|
122
123
|
# Adds an expectation that the specified method may be called any number of times with any parameters.
|
@@ -145,6 +146,7 @@ module Mocha
|
|
145
146
|
# object.stubs(:stubbed_method_one).returns(:result_one)
|
146
147
|
# object.stubs(:stubbed_method_two).returns(:result_two)
|
147
148
|
def stubs(method_name_or_hash, backtrace = nil)
|
149
|
+
expectation = nil
|
148
150
|
iterator = ArgumentIterator.new(method_name_or_hash)
|
149
151
|
iterator.each do |*args|
|
150
152
|
method_name = args.shift
|
@@ -154,6 +156,7 @@ module Mocha
|
|
154
156
|
expectation.returns(args.shift) unless args.empty?
|
155
157
|
@expectations.add(expectation)
|
156
158
|
end
|
159
|
+
expectation
|
157
160
|
end
|
158
161
|
|
159
162
|
# Removes the specified stubbed methods (added by calls to {#expects} or {#stubs}) and all expectations associated with them.
|
@@ -161,7 +164,7 @@ module Mocha
|
|
161
164
|
# @param [Array<Symbol>] method_names names of methods to unstub.
|
162
165
|
#
|
163
166
|
# @example Invoking an unstubbed method causes error to be raised
|
164
|
-
# object = mock('mock')
|
167
|
+
# object = mock('mock')
|
165
168
|
# object.stubs(:stubbed_method).returns(:result_one)
|
166
169
|
# object.stubbed_method # => :result_one
|
167
170
|
# object.unstub(:stubbed_method)
|
@@ -180,11 +183,11 @@ module Mocha
|
|
180
183
|
end
|
181
184
|
end
|
182
185
|
|
183
|
-
# Constrains the {Mock} instance so that it can only expect or stub methods to which +responder+ responds. The constraint is only applied at method invocation time.
|
186
|
+
# Constrains the {Mock} instance so that it can only expect or stub methods to which +responder+ responds publicly. The constraint is only applied at method invocation time.
|
184
187
|
#
|
185
|
-
# A +NoMethodError+ will be raised if the +responder+ does not +#respond_to?+
|
188
|
+
# A +NoMethodError+ will be raised if the +responder+ does not publicly +#respond_to?+ the invoked method (even if the method has been expected or stubbed).
|
186
189
|
#
|
187
|
-
# The {Mock} instance will delegate its +#respond_to?+ method to the +responder+.
|
190
|
+
# The {Mock} instance will delegate its +#respond_to?+ method to the +responder+. However, the +include_all+ parameter is not passed through, so only public methods on the +responder+ will be considered.
|
188
191
|
#
|
189
192
|
# Note that the methods on +responder+ are never actually invoked.
|
190
193
|
#
|
@@ -234,11 +237,11 @@ module Mocha
|
|
234
237
|
self
|
235
238
|
end
|
236
239
|
|
237
|
-
# Constrains the {Mock} instance so that it can only expect or stub methods to which an instance of the +responder_class+ responds. The constraint is only applied at method invocation time. Note that the responder instance is instantiated using +Class#allocate+.
|
240
|
+
# Constrains the {Mock} instance so that it can only expect or stub methods to which an instance of the +responder_class+ responds publicly. The constraint is only applied at method invocation time. Note that the responder instance is instantiated using +Class#allocate+.
|
238
241
|
#
|
239
|
-
# A +NoMethodError+ will be raised if the responder instance does not +#respond_to?+
|
242
|
+
# A +NoMethodError+ will be raised if the responder instance does not publicly +#respond_to?+ the invoked method (even if the method has been expected or stubbed).
|
240
243
|
#
|
241
|
-
# The {Mock} instance will delegate its +#respond_to?+ method to the responder instance.
|
244
|
+
# The {Mock} instance will delegate its +#respond_to?+ method to the responder instance. However, the +include_all+ parameter is not passed through, so only public methods on the +responder+ will be considered.
|
242
245
|
#
|
243
246
|
# Note that the methods on the responder instance are never actually invoked.
|
244
247
|
#
|
@@ -306,10 +309,18 @@ module Mocha
|
|
306
309
|
end
|
307
310
|
|
308
311
|
# @private
|
309
|
-
|
312
|
+
# rubocop:disable Style/MethodMissingSuper
|
313
|
+
def method_missing(symbol, *arguments, &block)
|
314
|
+
handle_method_call(symbol, arguments, block)
|
315
|
+
end
|
316
|
+
ruby2_keywords(:method_missing)
|
317
|
+
# rubocop:enable Style/MethodMissingSuper
|
318
|
+
|
319
|
+
# @private
|
320
|
+
def handle_method_call(symbol, arguments, block)
|
310
321
|
check_expiry
|
311
322
|
check_responder_responds_to(symbol)
|
312
|
-
invocation = Invocation.new(self, symbol,
|
323
|
+
invocation = Invocation.new(self, symbol, arguments, block)
|
313
324
|
if (matching_expectation_allowing_invocation = all_expectations.match_allowing_invocation(invocation))
|
314
325
|
matching_expectation_allowing_invocation.invoke(invocation)
|
315
326
|
elsif (matching_expectation = all_expectations.match(invocation)) || (!matching_expectation && !@everything_stubbed)
|
@@ -318,25 +329,14 @@ module Mocha
|
|
318
329
|
end
|
319
330
|
|
320
331
|
# @private
|
321
|
-
def respond_to_missing?(symbol,
|
332
|
+
def respond_to_missing?(symbol, _include_all)
|
322
333
|
if @responder
|
323
|
-
|
324
|
-
@responder.respond_to?(symbol, include_private)
|
325
|
-
else
|
326
|
-
@responder.respond_to?(symbol)
|
327
|
-
end
|
334
|
+
@responder.respond_to?(symbol)
|
328
335
|
else
|
329
336
|
@everything_stubbed || all_expectations.matches_method?(symbol)
|
330
337
|
end
|
331
338
|
end
|
332
339
|
|
333
|
-
if PRE_RUBY_V19
|
334
|
-
# @private
|
335
|
-
def respond_to?(symbol, include_private = false)
|
336
|
-
respond_to_missing?(symbol, include_private)
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
340
|
# @private
|
341
341
|
def __verified__?(assertion_counter = nil)
|
342
342
|
@expectations.verified?(assertion_counter)
|
@@ -387,14 +387,14 @@ module Mocha
|
|
387
387
|
end
|
388
388
|
|
389
389
|
def check_expiry
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
390
|
+
return unless @expired
|
391
|
+
|
392
|
+
sentences = [
|
393
|
+
"#{mocha_inspect} was instantiated in one test but it is receiving invocations within another test.",
|
394
|
+
'This can lead to unintended interactions between tests and hence unexpected test failures.',
|
395
|
+
'Ensure that every test correctly cleans up any state that it introduces.'
|
396
|
+
]
|
397
|
+
raise StubbingError.new(sentences.join(' '), caller)
|
398
398
|
end
|
399
399
|
end
|
400
400
|
end
|
data/lib/mocha/mockery.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'mocha/ruby_version'
|
2
1
|
require 'mocha/central'
|
3
2
|
require 'mocha/mock'
|
4
3
|
require 'mocha/names'
|
@@ -119,10 +118,9 @@ module Mocha
|
|
119
118
|
end
|
120
119
|
|
121
120
|
def on_stubbing(object, method)
|
122
|
-
method = PRE_RUBY_V19 ? method.to_s : method.to_sym
|
123
121
|
signature_proc = lambda { "#{object.mocha_inspect}.#{method}" }
|
124
122
|
check(:stubbing_non_existent_method, 'non-existent method', signature_proc) do
|
125
|
-
!(object.stubba_class.__method_exists__?(method, true) || object.respond_to?(method
|
123
|
+
!(object.stubba_class.__method_exists__?(method, true) || object.respond_to?(method))
|
126
124
|
end
|
127
125
|
check(:stubbing_non_public_method, 'non-public method', signature_proc) do
|
128
126
|
object.stubba_class.__method_exists__?(method, false)
|
data/lib/mocha/names.rb
CHANGED
@@ -3,7 +3,7 @@ module Mocha
|
|
3
3
|
# @abstract Subclass and implement +#matches?+ and +#mocha_inspect+ to define a custom matcher. Also add a suitably named instance method to {ParameterMatchers} to build an instance of the new matcher c.f. {#equals}.
|
4
4
|
class Base
|
5
5
|
# @private
|
6
|
-
def to_matcher
|
6
|
+
def to_matcher(_expectation = nil)
|
7
7
|
self
|
8
8
|
end
|
9
9
|
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'mocha/deprecation'
|
2
1
|
require 'mocha/parameter_matchers/base'
|
3
2
|
require 'uri'
|
4
3
|
require 'cgi'
|
@@ -51,7 +50,7 @@ module Mocha
|
|
51
50
|
# @private
|
52
51
|
def explode(uri)
|
53
52
|
query_hash = CGI.parse(uri.query || '')
|
54
|
-
URI::Generic::COMPONENT.inject({}) { |h, k| h.merge(k => uri.__send__(k)) }.merge(:
|
53
|
+
URI::Generic::COMPONENT.inject({}) { |h, k| h.merge(k => uri.__send__(k)) }.merge(query: query_hash)
|
55
54
|
end
|
56
55
|
end
|
57
56
|
end
|
@@ -42,20 +42,10 @@ module Mocha
|
|
42
42
|
#
|
43
43
|
def has_entry(*options) # rubocop:disable Naming/PredicateName
|
44
44
|
case options.length
|
45
|
+
when 0
|
46
|
+
raise ArgumentError, 'No arguments. Expecting at least one.'
|
45
47
|
when 1
|
46
|
-
|
47
|
-
when Hash
|
48
|
-
case options[0].length
|
49
|
-
when 0
|
50
|
-
raise ArgumentError, 'Argument has no entries.'
|
51
|
-
when 1
|
52
|
-
key, value = options[0].first
|
53
|
-
else
|
54
|
-
raise ArgumentError, 'Argument has multiple entries. Use Mocha::ParameterMatchers#has_entries instead.'
|
55
|
-
end
|
56
|
-
else
|
57
|
-
raise ArgumentError, 'Argument is not a Hash.'
|
58
|
-
end
|
48
|
+
key, value = parse_option(options[0])
|
59
49
|
when 2
|
60
50
|
key, value = options
|
61
51
|
else
|
@@ -85,5 +75,24 @@ module Mocha
|
|
85
75
|
"has_entry(#{@key.mocha_inspect} => #{@value.mocha_inspect})"
|
86
76
|
end
|
87
77
|
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# @private
|
82
|
+
def parse_option(option)
|
83
|
+
case option
|
84
|
+
when Hash
|
85
|
+
case option.length
|
86
|
+
when 0
|
87
|
+
raise ArgumentError, 'Argument has no entries.'
|
88
|
+
when 1
|
89
|
+
option.first
|
90
|
+
else
|
91
|
+
raise ArgumentError, 'Argument has multiple entries. Use Mocha::ParameterMatchers#has_entries instead.'
|
92
|
+
end
|
93
|
+
else
|
94
|
+
raise ArgumentError, 'Argument is not a Hash.'
|
95
|
+
end
|
96
|
+
end
|
88
97
|
end
|
89
98
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'mocha/parameter_matchers/base'
|
2
|
+
|
3
|
+
module Mocha
|
4
|
+
module ParameterMatchers
|
5
|
+
# Matches +Hash+ containing +keys+.
|
6
|
+
#
|
7
|
+
# @param [*Array<Object>] keys expected keys.
|
8
|
+
# @return [HasKeys] parameter matcher.
|
9
|
+
#
|
10
|
+
# @see Expectation#with
|
11
|
+
#
|
12
|
+
# @example Actual parameter contains entry with expected keys.
|
13
|
+
# object = mock()
|
14
|
+
# object.expects(:method_1).with(has_keys(:key_1, :key_2))
|
15
|
+
# object.method_1(:key_1 => 1, :key_2 => 2, :key_3 => 3)
|
16
|
+
# # no error raised
|
17
|
+
#
|
18
|
+
# @example Actual parameter does not contain all expected keys.
|
19
|
+
# object = mock()
|
20
|
+
# object.expects(:method_1).with(has_keys(:key_1, :key_2))
|
21
|
+
# object.method_1(:key_2 => 2)
|
22
|
+
# # error raised, because method_1 was not called with Hash containing key: :key_1
|
23
|
+
#
|
24
|
+
def has_keys(*keys) # rubocop:disable Naming/PredicateName
|
25
|
+
HasKeys.new(*keys)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Parameter matcher which matches when actual parameter contains +Hash+ with all expected keys.
|
29
|
+
class HasKeys < Base
|
30
|
+
# @private
|
31
|
+
def initialize(*keys)
|
32
|
+
raise ArgumentError, 'No arguments. Expecting at least one.' if keys.empty?
|
33
|
+
|
34
|
+
@keys = keys
|
35
|
+
end
|
36
|
+
|
37
|
+
# @private
|
38
|
+
def matches?(available_parameters)
|
39
|
+
parameter = available_parameters.shift
|
40
|
+
return false unless parameter.respond_to?(:keys)
|
41
|
+
|
42
|
+
@keys.map(&:to_matcher).all? do |matcher|
|
43
|
+
parameter.keys.any? { |key| matcher.matches?([key]) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @private
|
48
|
+
def mocha_inspect
|
49
|
+
"has_keys(#{@keys.mocha_inspect(false)})"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'mocha/parameter_matchers/equals'
|
2
|
+
require 'mocha/parameter_matchers/positional_or_keyword_hash'
|
2
3
|
|
3
4
|
module Mocha
|
4
5
|
module ParameterMatchers
|
5
6
|
# @private
|
6
7
|
module InstanceMethods
|
7
8
|
# @private
|
8
|
-
def to_matcher
|
9
|
+
def to_matcher(_expectation = nil)
|
9
10
|
Mocha::ParameterMatchers::Equals.new(self)
|
10
11
|
end
|
11
12
|
end
|
@@ -16,3 +17,11 @@ end
|
|
16
17
|
class Object
|
17
18
|
include Mocha::ParameterMatchers::InstanceMethods
|
18
19
|
end
|
20
|
+
|
21
|
+
# @private
|
22
|
+
class Hash
|
23
|
+
# @private
|
24
|
+
def to_matcher(expectation = nil)
|
25
|
+
Mocha::ParameterMatchers::PositionalOrKeywordHash.new(self, expectation)
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'mocha/configuration'
|
2
|
+
require 'mocha/deprecation'
|
3
|
+
require 'mocha/parameter_matchers/base'
|
4
|
+
|
5
|
+
module Mocha
|
6
|
+
module ParameterMatchers
|
7
|
+
# @private
|
8
|
+
class PositionalOrKeywordHash < Base
|
9
|
+
def initialize(value, expectation)
|
10
|
+
@value = value
|
11
|
+
@expectation = expectation
|
12
|
+
end
|
13
|
+
|
14
|
+
def matches?(available_parameters)
|
15
|
+
parameter, is_last_parameter = extract_parameter(available_parameters)
|
16
|
+
return false unless parameter == @value
|
17
|
+
|
18
|
+
if is_last_parameter && !same_type_of_hash?(parameter, @value)
|
19
|
+
return false if Mocha.configuration.strict_keyword_argument_matching?
|
20
|
+
|
21
|
+
deprecation_warning(parameter, @value) if Mocha::RUBY_V27_PLUS
|
22
|
+
end
|
23
|
+
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def mocha_inspect
|
28
|
+
@value.mocha_inspect
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def extract_parameter(available_parameters)
|
34
|
+
[available_parameters.shift, available_parameters.empty?]
|
35
|
+
end
|
36
|
+
|
37
|
+
def same_type_of_hash?(actual, expected)
|
38
|
+
ruby2_keywords_hash?(actual) == ruby2_keywords_hash?(expected)
|
39
|
+
end
|
40
|
+
|
41
|
+
def deprecation_warning(actual, expected)
|
42
|
+
details1 = "Expectation #{expectation_definition} expected #{hash_type(expected)} (#{expected.mocha_inspect}),".squeeze(' ')
|
43
|
+
details2 = "but received #{hash_type(actual)} (#{actual.mocha_inspect})."
|
44
|
+
sentence1 = 'These will stop matching when strict keyword argument matching is enabled.'
|
45
|
+
sentence2 = 'See the documentation for Mocha::Configuration#strict_keyword_argument_matching=.'
|
46
|
+
Deprecation.warning([details1, details2, sentence1, sentence2].join(' '))
|
47
|
+
end
|
48
|
+
|
49
|
+
def hash_type(hash)
|
50
|
+
ruby2_keywords_hash?(hash) ? 'keyword arguments' : 'positional hash'
|
51
|
+
end
|
52
|
+
|
53
|
+
def ruby2_keywords_hash?(hash)
|
54
|
+
hash.is_a?(Hash) && ::Hash.ruby2_keywords_hash?(hash)
|
55
|
+
end
|
56
|
+
|
57
|
+
def expectation_definition
|
58
|
+
return nil unless @expectation
|
59
|
+
|
60
|
+
"defined at #{@expectation.definition_location}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -13,6 +13,7 @@ require 'mocha/parameter_matchers/equals'
|
|
13
13
|
require 'mocha/parameter_matchers/has_entry'
|
14
14
|
require 'mocha/parameter_matchers/has_entries'
|
15
15
|
require 'mocha/parameter_matchers/has_key'
|
16
|
+
require 'mocha/parameter_matchers/has_keys'
|
16
17
|
require 'mocha/parameter_matchers/has_value'
|
17
18
|
require 'mocha/parameter_matchers/includes'
|
18
19
|
require 'mocha/parameter_matchers/instance_of'
|
@@ -3,8 +3,9 @@ require 'mocha/parameter_matchers'
|
|
3
3
|
|
4
4
|
module Mocha
|
5
5
|
class ParametersMatcher
|
6
|
-
def initialize(expected_parameters = [ParameterMatchers::AnyParameters.new], &matching_block)
|
6
|
+
def initialize(expected_parameters = [ParameterMatchers::AnyParameters.new], expectation = nil, &matching_block)
|
7
7
|
@expected_parameters = expected_parameters
|
8
|
+
@expectation = expectation
|
8
9
|
@matching_block = matching_block
|
9
10
|
end
|
10
11
|
|
@@ -23,12 +24,11 @@ module Mocha
|
|
23
24
|
def mocha_inspect
|
24
25
|
signature = matchers.mocha_inspect
|
25
26
|
signature = signature.gsub(/^\[|\]$/, '')
|
26
|
-
signature = signature.gsub(/^\{|\}$/, '') if matchers.length == 1
|
27
27
|
"(#{signature})"
|
28
28
|
end
|
29
29
|
|
30
30
|
def matchers
|
31
|
-
@expected_parameters.map(
|
31
|
+
@expected_parameters.map { |p| p.to_matcher(@expectation) }
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
data/lib/mocha/ruby_version.rb
CHANGED