mocha 1.16.1 → 2.7.1
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 +10 -13
- data/.yardopts +1 -2
- data/COPYING.md +2 -2
- data/Gemfile +7 -4
- data/MIT-LICENSE.md +1 -1
- data/README.md +23 -24
- data/RELEASE.md +294 -0
- data/Rakefile +23 -24
- data/lib/mocha/api.rb +30 -71
- data/lib/mocha/backtrace_filter.rb +2 -2
- data/lib/mocha/cardinality.rb +4 -0
- data/lib/mocha/configuration.rb +44 -126
- data/lib/mocha/debug.rb +2 -5
- data/lib/mocha/detection/{mini_test.rb → minitest.rb} +5 -5
- data/lib/mocha/detection/test_unit.rb +2 -2
- data/lib/mocha/expectation.rb +99 -12
- data/lib/mocha/expectation_error_factory.rb +2 -2
- data/lib/mocha/expectation_list.rb +8 -6
- data/lib/mocha/hooks.rb +10 -4
- data/lib/mocha/inspect.rb +15 -4
- data/lib/mocha/integration/{mini_test → minitest}/adapter.rb +21 -6
- data/lib/mocha/integration/{mini_test → minitest}/exception_translation.rb +2 -2
- data/lib/mocha/integration/minitest.rb +28 -0
- data/lib/mocha/integration/test_unit/adapter.rb +9 -4
- data/lib/mocha/integration/test_unit.rb +10 -31
- data/lib/mocha/invocation.rb +2 -15
- data/lib/mocha/minitest.rb +3 -6
- data/lib/mocha/mock.rb +45 -18
- data/lib/mocha/mockery.rb +13 -9
- data/lib/mocha/names.rb +1 -1
- data/lib/mocha/object_methods.rb +2 -2
- data/lib/mocha/parameter_matchers/base.rb +4 -9
- data/lib/mocha/parameter_matchers/equivalent_uri.rb +1 -2
- data/lib/mocha/parameter_matchers/has_entries.rb +7 -2
- data/lib/mocha/parameter_matchers/includes.rb +3 -3
- data/lib/mocha/parameter_matchers/instance_methods.rb +10 -2
- data/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb +66 -0
- data/lib/mocha/parameter_matchers/responds_with.rb +32 -5
- data/lib/mocha/parameters_matcher.rb +10 -6
- data/lib/mocha/ruby_version.rb +2 -9
- data/lib/mocha/stubbed_method.rb +3 -39
- data/lib/mocha/test_unit.rb +1 -4
- data/lib/mocha/version.rb +1 -1
- data/mocha.gemspec +11 -1
- metadata +31 -31
- 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/mini_test.rb +0 -56
- 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_186_and_above.rb +0 -63
- data/lib/mocha/integration.rb +0 -11
- data/lib/mocha/setup.rb +0 -14
- data/yard-templates/default/layout/html/google_analytics.erb +0 -8
- data/yard-templates/default/layout/html/setup.rb +0 -5
data/lib/mocha/mock.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'ruby2_keywords'
|
1
2
|
require 'mocha/expectation'
|
2
3
|
require 'mocha/expectation_list'
|
3
4
|
require 'mocha/invocation'
|
@@ -7,6 +8,7 @@ require 'mocha/method_matcher'
|
|
7
8
|
require 'mocha/parameters_matcher'
|
8
9
|
require 'mocha/argument_iterator'
|
9
10
|
require 'mocha/expectation_error_factory'
|
11
|
+
require 'mocha/deprecation'
|
10
12
|
|
11
13
|
module Mocha
|
12
14
|
# Traditional mock object.
|
@@ -33,6 +35,9 @@ module Mocha
|
|
33
35
|
# while an +expects(:foo).at_least_once+ expectation will always be matched
|
34
36
|
# against invocations.
|
35
37
|
#
|
38
|
+
# However, note that if the expectation that matches the invocation has a
|
39
|
+
# cardinality of "never", then an unexpected invocation error is reported.
|
40
|
+
#
|
36
41
|
# This scheme allows you to:
|
37
42
|
#
|
38
43
|
# - Set up default stubs in your the +setup+ method of your test class and
|
@@ -99,7 +104,7 @@ module Mocha
|
|
99
104
|
#
|
100
105
|
# @example Setup multiple expectations using +expected_methods_vs_return_values+.
|
101
106
|
# object = mock()
|
102
|
-
# object.expects(:
|
107
|
+
# object.expects(expected_method_one: :result_one, expected_method_two: :result_two)
|
103
108
|
#
|
104
109
|
# # is exactly equivalent to
|
105
110
|
#
|
@@ -113,6 +118,7 @@ module Mocha
|
|
113
118
|
method_name = args.shift
|
114
119
|
ensure_method_not_already_defined(method_name)
|
115
120
|
expectation = Expectation.new(self, method_name, backtrace)
|
121
|
+
expectation.in_sequence(@mockery.sequences.last) if @mockery.sequences.any?
|
116
122
|
expectation.returns(args.shift) unless args.empty?
|
117
123
|
@expectations.add(expectation)
|
118
124
|
end
|
@@ -137,7 +143,7 @@ module Mocha
|
|
137
143
|
#
|
138
144
|
# @example Setup multiple expectations using +stubbed_methods_vs_return_values+.
|
139
145
|
# object = mock()
|
140
|
-
# object.stubs(:
|
146
|
+
# object.stubs(stubbed_method_one: :result_one, stubbed_method_two: :result_two)
|
141
147
|
#
|
142
148
|
# # is exactly equivalent to
|
143
149
|
#
|
@@ -152,6 +158,7 @@ module Mocha
|
|
152
158
|
ensure_method_not_already_defined(method_name)
|
153
159
|
expectation = Expectation.new(self, method_name, backtrace)
|
154
160
|
expectation.at_least(0)
|
161
|
+
expectation.in_sequence(@mockery.sequences.last) if @mockery.sequences.any?
|
155
162
|
expectation.returns(args.shift) unless args.empty?
|
156
163
|
@expectations.add(expectation)
|
157
164
|
end
|
@@ -311,16 +318,30 @@ module Mocha
|
|
311
318
|
def method_missing(symbol, *arguments, &block) # rubocop:disable Style/MethodMissingSuper
|
312
319
|
handle_method_call(symbol, arguments, block)
|
313
320
|
end
|
321
|
+
ruby2_keywords(:method_missing)
|
314
322
|
|
315
323
|
# @private
|
316
|
-
def handle_method_call(symbol, arguments, block)
|
324
|
+
def handle_method_call(symbol, arguments, block) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
317
325
|
check_expiry
|
318
326
|
check_responder_responds_to(symbol)
|
319
327
|
invocation = Invocation.new(self, symbol, arguments, block)
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
328
|
+
|
329
|
+
matching_expectations = all_expectations.matching_expectations(invocation)
|
330
|
+
|
331
|
+
index = 0
|
332
|
+
while index < matching_expectations.length
|
333
|
+
matching_expectation = matching_expectations[index]
|
334
|
+
if matching_expectation.invocations_never_allowed?
|
335
|
+
raise_unexpected_invocation_error(invocation, matching_expectation)
|
336
|
+
elsif matching_expectation.invocations_allowed?
|
337
|
+
return matching_expectation.invoke(invocation)
|
338
|
+
end
|
339
|
+
index += 1
|
340
|
+
end
|
341
|
+
|
342
|
+
matching_expectation_ignoring_order = all_expectations.match(invocation, ignoring_order: true)
|
343
|
+
if matching_expectation_ignoring_order || (!matching_expectation_ignoring_order && !@everything_stubbed) # rubocop:disable Style/GuardClause
|
344
|
+
raise_unexpected_invocation_error(invocation, matching_expectation_ignoring_order)
|
324
345
|
end
|
325
346
|
end
|
326
347
|
|
@@ -339,8 +360,8 @@ module Mocha
|
|
339
360
|
end
|
340
361
|
|
341
362
|
# @private
|
342
|
-
def __expire__
|
343
|
-
@expired = true
|
363
|
+
def __expire__(origin)
|
364
|
+
@expired = origin || true
|
344
365
|
end
|
345
366
|
|
346
367
|
# @private
|
@@ -369,7 +390,11 @@ module Mocha
|
|
369
390
|
if @unexpected_invocation.nil?
|
370
391
|
@unexpected_invocation = invocation
|
371
392
|
matching_expectation.invoke(invocation) if matching_expectation
|
372
|
-
|
393
|
+
call_description = @unexpected_invocation.call_description
|
394
|
+
if matching_expectation && !matching_expectation.in_correct_order?
|
395
|
+
call_description += ' invoked out of order'
|
396
|
+
end
|
397
|
+
message = "#{call_description}\n#{@mockery.mocha_inspect}"
|
373
398
|
else
|
374
399
|
message = @unexpected_invocation.short_call_description
|
375
400
|
end
|
@@ -383,14 +408,16 @@ module Mocha
|
|
383
408
|
end
|
384
409
|
|
385
410
|
def check_expiry
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
411
|
+
return unless @expired
|
412
|
+
|
413
|
+
origin = @expired == true ? 'one test' : @expired
|
414
|
+
|
415
|
+
sentences = [
|
416
|
+
"#{mocha_inspect} was instantiated in #{origin} but it is receiving invocations within another test.",
|
417
|
+
'This can lead to unintended interactions between tests and hence unexpected test failures.',
|
418
|
+
'Ensure that every test correctly cleans up any state that it introduces.'
|
419
|
+
]
|
420
|
+
raise StubbingError.new(sentences.join(' '), caller)
|
394
421
|
end
|
395
422
|
end
|
396
423
|
end
|
data/lib/mocha/mockery.rb
CHANGED
@@ -48,8 +48,8 @@ module Mocha
|
|
48
48
|
instance.verify(*args)
|
49
49
|
end
|
50
50
|
|
51
|
-
def teardown
|
52
|
-
instance.teardown
|
51
|
+
def teardown(origin = nil)
|
52
|
+
instance.teardown(origin)
|
53
53
|
ensure
|
54
54
|
@instances.pop
|
55
55
|
end
|
@@ -91,9 +91,9 @@ module Mocha
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
-
def teardown
|
94
|
+
def teardown(origin = nil)
|
95
95
|
stubba.unstub_all
|
96
|
-
mocks.each(
|
96
|
+
mocks.each { |m| m.__expire__(origin) }
|
97
97
|
reset
|
98
98
|
end
|
99
99
|
|
@@ -109,12 +109,16 @@ module Mocha
|
|
109
109
|
@state_machines ||= []
|
110
110
|
end
|
111
111
|
|
112
|
+
def sequences
|
113
|
+
@sequences ||= []
|
114
|
+
end
|
115
|
+
|
112
116
|
def mocha_inspect
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
117
|
+
lines = []
|
118
|
+
lines << "unsatisfied expectations:\n- #{unsatisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if unsatisfied_expectations.any?
|
119
|
+
lines << "satisfied expectations:\n- #{satisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if satisfied_expectations.any?
|
120
|
+
lines << "states:\n- #{state_machines.map(&:mocha_inspect).join("\n- ")}\n" if state_machines.any?
|
121
|
+
lines.join
|
118
122
|
end
|
119
123
|
|
120
124
|
def on_stubbing(object, method)
|
data/lib/mocha/names.rb
CHANGED
data/lib/mocha/object_methods.rb
CHANGED
@@ -59,7 +59,7 @@ module Mocha
|
|
59
59
|
#
|
60
60
|
# @example Setting up multiple expectations on a non-mock object.
|
61
61
|
# product = Product.new
|
62
|
-
# product.expects(
|
62
|
+
# product.expects(valid?: true, save: true)
|
63
63
|
#
|
64
64
|
# # exactly equivalent to
|
65
65
|
#
|
@@ -108,7 +108,7 @@ module Mocha
|
|
108
108
|
#
|
109
109
|
# @example Setting up multiple stubbed methods on a non-mock object.
|
110
110
|
# product = Product.new
|
111
|
-
# product.stubs(
|
111
|
+
# product.stubs(valid?: true, save: true)
|
112
112
|
#
|
113
113
|
# # exactly equivalent to
|
114
114
|
#
|
@@ -2,11 +2,6 @@ module Mocha
|
|
2
2
|
module ParameterMatchers
|
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
|
-
# @private
|
6
|
-
def to_matcher
|
7
|
-
self
|
8
|
-
end
|
9
|
-
|
10
5
|
# A shorthand way of combining two matchers when both must match.
|
11
6
|
#
|
12
7
|
# Returns a new {AllOf} parameter matcher combining two matchers using a logical AND.
|
@@ -21,12 +16,12 @@ module Mocha
|
|
21
16
|
# @example Alternative ways to combine matchers with a logical AND.
|
22
17
|
# object = mock()
|
23
18
|
# object.expects(:run).with(all_of(has_key(:foo), has_key(:bar)))
|
24
|
-
# object.run(:
|
19
|
+
# object.run(foo: 'foovalue', bar: 'barvalue')
|
25
20
|
#
|
26
21
|
# # is exactly equivalent to
|
27
22
|
#
|
28
23
|
# object.expects(:run).with(has_key(:foo) & has_key(:bar))
|
29
|
-
# object.run(:
|
24
|
+
# object.run(foo: 'foovalue', bar: 'barvalue)
|
30
25
|
def &(other)
|
31
26
|
AllOf.new(self, other)
|
32
27
|
end
|
@@ -45,12 +40,12 @@ module Mocha
|
|
45
40
|
# @example Alternative ways to combine matchers with a logical OR.
|
46
41
|
# object = mock()
|
47
42
|
# object.expects(:run).with(any_of(has_key(:foo), has_key(:bar)))
|
48
|
-
# object.run(:
|
43
|
+
# object.run(foo: 'foovalue')
|
49
44
|
#
|
50
45
|
# # is exactly equivalent to
|
51
46
|
#
|
52
47
|
# object.expects(:run).with(has_key(:foo) | has_key(:bar))
|
53
|
-
# object.run(:
|
48
|
+
# object.run(foo: 'foovalue')
|
54
49
|
#
|
55
50
|
# @example Using an explicit {Equals} matcher in combination with {#|}.
|
56
51
|
# object.expects(:run).with(equals(1) | equals(2))
|
@@ -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
|
@@ -30,20 +30,25 @@ module Mocha
|
|
30
30
|
# Parameter matcher which matches when actual parameter contains all expected +Hash+ entries.
|
31
31
|
class HasEntries < Base
|
32
32
|
# @private
|
33
|
-
def initialize(entries)
|
33
|
+
def initialize(entries, exact: false)
|
34
34
|
@entries = entries
|
35
|
+
@exact = exact
|
35
36
|
end
|
36
37
|
|
37
38
|
# @private
|
38
39
|
def matches?(available_parameters)
|
39
40
|
parameter = available_parameters.shift
|
41
|
+
return false unless parameter
|
42
|
+
return false unless parameter.respond_to?(:keys)
|
43
|
+
return false if @exact && @entries.length != parameter.keys.length
|
44
|
+
|
40
45
|
has_entry_matchers = @entries.map { |key, value| HasEntry.new(key, value) }
|
41
46
|
AllOf.new(*has_entry_matchers).matches?([parameter])
|
42
47
|
end
|
43
48
|
|
44
49
|
# @private
|
45
50
|
def mocha_inspect
|
46
|
-
"has_entries(#{@entries.mocha_inspect})"
|
51
|
+
@exact ? @entries.mocha_inspect : "has_entries(#{@entries.mocha_inspect})"
|
47
52
|
end
|
48
53
|
end
|
49
54
|
end
|
@@ -24,7 +24,7 @@ module Mocha
|
|
24
24
|
# @example Actual parameter includes item which matches nested matcher.
|
25
25
|
# object = mock()
|
26
26
|
# object.expects(:method_1).with(includes(has_key(:key)))
|
27
|
-
# object.method_1(['foo', 'bar', {:
|
27
|
+
# object.method_1(['foo', 'bar', {key: 'baz'}])
|
28
28
|
# # no error raised
|
29
29
|
#
|
30
30
|
# @example Actual parameter does not include item matching nested matcher.
|
@@ -44,11 +44,11 @@ module Mocha
|
|
44
44
|
# @example Actual parameter is a Hash including the given key.
|
45
45
|
# object = mock()
|
46
46
|
# object.expects(:method_1).with(includes(:bar))
|
47
|
-
# object.method_1({:
|
47
|
+
# object.method_1({foo: 1, bar: 2})
|
48
48
|
# # no error raised
|
49
49
|
#
|
50
50
|
# @example Actual parameter is a Hash without the given key.
|
51
|
-
# object.method_1({:
|
51
|
+
# object.method_1({foo: 1, baz: 2})
|
52
52
|
# # error raised, because hash does not include key 'bar'
|
53
53
|
#
|
54
54
|
# @example Actual parameter is a Hash with a key matching the given matcher.
|
@@ -1,12 +1,20 @@
|
|
1
|
+
require 'mocha/parameter_matchers/base'
|
1
2
|
require 'mocha/parameter_matchers/equals'
|
3
|
+
require 'mocha/parameter_matchers/positional_or_keyword_hash'
|
2
4
|
|
3
5
|
module Mocha
|
4
6
|
module ParameterMatchers
|
5
7
|
# @private
|
6
8
|
module InstanceMethods
|
7
9
|
# @private
|
8
|
-
def to_matcher
|
9
|
-
|
10
|
+
def to_matcher(expectation: nil, top_level: false)
|
11
|
+
if Base === self
|
12
|
+
self
|
13
|
+
elsif Hash === self && top_level
|
14
|
+
Mocha::ParameterMatchers::PositionalOrKeywordHash.new(self, expectation)
|
15
|
+
else
|
16
|
+
Mocha::ParameterMatchers::Equals.new(self)
|
17
|
+
end
|
10
18
|
end
|
11
19
|
end
|
12
20
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'mocha/configuration'
|
2
|
+
require 'mocha/deprecation'
|
3
|
+
require 'mocha/parameter_matchers/base'
|
4
|
+
require 'mocha/parameter_matchers/has_entries'
|
5
|
+
|
6
|
+
module Mocha
|
7
|
+
module ParameterMatchers
|
8
|
+
# @private
|
9
|
+
class PositionalOrKeywordHash < Base
|
10
|
+
def initialize(value, expectation)
|
11
|
+
@value = value
|
12
|
+
@expectation = expectation
|
13
|
+
end
|
14
|
+
|
15
|
+
def matches?(available_parameters)
|
16
|
+
parameter, is_last_parameter = extract_parameter(available_parameters)
|
17
|
+
|
18
|
+
return false unless HasEntries.new(@value, exact: true).matches?([parameter])
|
19
|
+
|
20
|
+
if is_last_parameter && !same_type_of_hash?(parameter, @value)
|
21
|
+
return false if Mocha.configuration.strict_keyword_argument_matching?
|
22
|
+
|
23
|
+
deprecation_warning(parameter, @value) if Mocha::RUBY_V27_PLUS
|
24
|
+
end
|
25
|
+
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def mocha_inspect
|
30
|
+
@value.mocha_inspect
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def extract_parameter(available_parameters)
|
36
|
+
[available_parameters.shift, available_parameters.empty?]
|
37
|
+
end
|
38
|
+
|
39
|
+
def same_type_of_hash?(actual, expected)
|
40
|
+
ruby2_keywords_hash?(actual) == ruby2_keywords_hash?(expected)
|
41
|
+
end
|
42
|
+
|
43
|
+
def deprecation_warning(actual, expected)
|
44
|
+
details1 = "Expectation #{expectation_definition} expected #{hash_type(expected)} (#{expected.mocha_inspect}),".squeeze(' ')
|
45
|
+
details2 = "but received #{hash_type(actual)} (#{actual.mocha_inspect})."
|
46
|
+
sentence1 = 'These will stop matching when strict keyword argument matching is enabled.'
|
47
|
+
sentence2 = 'See the documentation for Mocha::Configuration#strict_keyword_argument_matching=.'
|
48
|
+
Deprecation.warning([details1, details2, sentence1, sentence2].join(' '))
|
49
|
+
end
|
50
|
+
|
51
|
+
def hash_type(hash)
|
52
|
+
ruby2_keywords_hash?(hash) ? 'keyword arguments' : 'positional hash'
|
53
|
+
end
|
54
|
+
|
55
|
+
def ruby2_keywords_hash?(hash)
|
56
|
+
hash.is_a?(Hash) && ::Hash.ruby2_keywords_hash?(hash)
|
57
|
+
end
|
58
|
+
|
59
|
+
def expectation_definition
|
60
|
+
return nil unless @expectation
|
61
|
+
|
62
|
+
"defined at #{@expectation.definition_location}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -1,12 +1,18 @@
|
|
1
1
|
require 'mocha/parameter_matchers/base'
|
2
|
+
require 'mocha/parameter_matchers/all_of'
|
2
3
|
require 'yaml'
|
3
4
|
|
4
5
|
module Mocha
|
5
6
|
module ParameterMatchers
|
6
|
-
#
|
7
|
+
# @overload def responds_with(message, result)
|
8
|
+
# Matches any object that responds to +message+ with +result+. To put it another way, it tests the quack, not the duck.
|
9
|
+
# @param [Symbol] message method to invoke.
|
10
|
+
# @param [Object] result expected result of sending +message+.
|
11
|
+
# @overload def responds_with(messages_vs_results)
|
12
|
+
# Matches any object that responds to all the messages with the corresponding results as specified by +messages_vs_results+.
|
13
|
+
# @param [Hash<Symbol,Object>] messages_vs_results +Hash+ of messages vs results.
|
14
|
+
# @raise [ArgumentError] if +messages_vs_results+ does not contain at least one entry.
|
7
15
|
#
|
8
|
-
# @param [Symbol] message method to invoke.
|
9
|
-
# @param [Object] result expected result of sending +message+.
|
10
16
|
# @return [RespondsWith] parameter matcher.
|
11
17
|
#
|
12
18
|
# @see Expectation#with
|
@@ -22,8 +28,29 @@ module Mocha
|
|
22
28
|
# object.expects(:method_1).with(responds_with(:upcase, "BAR"))
|
23
29
|
# object.method_1("foo")
|
24
30
|
# # error raised, because "foo".upcase != "BAR"
|
25
|
-
|
26
|
-
|
31
|
+
#
|
32
|
+
# @example Actual parameter responds with "FOO" when :upcase is invoked and "oof" when :reverse is invoked.
|
33
|
+
# object = mock()
|
34
|
+
# object.expects(:method_1).with(responds_with(upcase: "FOO", reverse: "oof"))
|
35
|
+
# object.method_1("foo")
|
36
|
+
# # no error raised, because "foo".upcase == "FOO" and "foo".reverse == "oof"
|
37
|
+
def responds_with(*options)
|
38
|
+
case options.length
|
39
|
+
when 0
|
40
|
+
raise ArgumentError, 'No arguments. Expecting at least one.'
|
41
|
+
when 1
|
42
|
+
option = options.first
|
43
|
+
raise ArgumentError, 'Argument is not a Hash.' unless option.is_a?(Hash)
|
44
|
+
raise ArgumentError, 'Argument has no entries.' if option.empty?
|
45
|
+
|
46
|
+
matchers = option.map { |message, result| RespondsWith.new(message, result) }
|
47
|
+
AllOf.new(*matchers)
|
48
|
+
when 2
|
49
|
+
message, result = options
|
50
|
+
RespondsWith.new(message, result)
|
51
|
+
else
|
52
|
+
raise ArgumentError, 'Too many arguments; use either a single argument (must be a Hash) or two arguments (a message and a result).'
|
53
|
+
end
|
27
54
|
end
|
28
55
|
|
29
56
|
# Parameter matcher which matches if actual parameter returns expected result when specified method is invoked.
|
@@ -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
|
|
@@ -21,14 +22,17 @@ module Mocha
|
|
21
22
|
end
|
22
23
|
|
23
24
|
def mocha_inspect
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
if @matching_block
|
26
|
+
'(arguments_accepted_by_custom_matching_block)'
|
27
|
+
else
|
28
|
+
signature = matchers.mocha_inspect
|
29
|
+
signature = signature.gsub(/^\[|\]$/, '')
|
30
|
+
"(#{signature})"
|
31
|
+
end
|
28
32
|
end
|
29
33
|
|
30
34
|
def matchers
|
31
|
-
@expected_parameters.map(
|
35
|
+
@expected_parameters.map { |p| p.to_matcher(expectation: @expectation, top_level: true) }
|
32
36
|
end
|
33
37
|
end
|
34
38
|
end
|
data/lib/mocha/ruby_version.rb
CHANGED
@@ -1,11 +1,4 @@
|
|
1
|
-
require 'mocha/deprecation'
|
2
|
-
|
3
1
|
module Mocha
|
4
|
-
|
5
|
-
|
6
|
-
unless RUBY_V2_PLUS
|
7
|
-
Mocha::Deprecation.warning(
|
8
|
-
'Versions of Ruby earlier than v2.0 will not be supported in future versions of Mocha.'
|
9
|
-
)
|
10
|
-
end
|
2
|
+
RUBY_V27_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.7')
|
3
|
+
RUBY_V34_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('3.4')
|
11
4
|
end
|
data/lib/mocha/stubbed_method.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'ruby2_keywords'
|
1
2
|
require 'mocha/ruby_version'
|
2
3
|
|
3
4
|
module Mocha
|
@@ -20,7 +21,6 @@ module Mocha
|
|
20
21
|
|
21
22
|
def unstub
|
22
23
|
remove_new_method
|
23
|
-
restore_original_method
|
24
24
|
mock.unstub(method_name.to_sym)
|
25
25
|
return if mock.any_expectations?
|
26
26
|
reset_mocha
|
@@ -37,20 +37,7 @@ module Mocha
|
|
37
37
|
def hide_original_method
|
38
38
|
return unless original_method_owner.__method_exists__?(method_name)
|
39
39
|
store_original_method_visibility
|
40
|
-
|
41
|
-
use_prepended_module_for_stub_method
|
42
|
-
else
|
43
|
-
begin
|
44
|
-
store_original_method
|
45
|
-
# rubocop:disable Lint/HandleExceptions
|
46
|
-
rescue NameError
|
47
|
-
# deal with nasties like ActiveRecord::Associations::AssociationProxy
|
48
|
-
end
|
49
|
-
# rubocop:enable Lint/HandleExceptions
|
50
|
-
if stub_method_overwrites_original_method?
|
51
|
-
remove_original_method_from_stubbee
|
52
|
-
end
|
53
|
-
end
|
40
|
+
use_prepended_module_for_stub_method
|
54
41
|
end
|
55
42
|
|
56
43
|
def define_new_method
|
@@ -59,6 +46,7 @@ module Mocha
|
|
59
46
|
stub_method_owner.send(:define_method, method_name) do |*args, &block|
|
60
47
|
self_in_scope.mock.handle_method_call(method_name_in_scope, args, block)
|
61
48
|
end
|
49
|
+
stub_method_owner.send(:ruby2_keywords, method_name)
|
62
50
|
retain_original_visibility(stub_method_owner)
|
63
51
|
end
|
64
52
|
|
@@ -66,18 +54,6 @@ module Mocha
|
|
66
54
|
stub_method_owner.send(:remove_method, method_name)
|
67
55
|
end
|
68
56
|
|
69
|
-
def store_original_method
|
70
|
-
@original_method = stubbee_method(method_name)
|
71
|
-
end
|
72
|
-
|
73
|
-
def restore_original_method
|
74
|
-
return if use_prepended_module_for_stub_method?
|
75
|
-
if stub_method_overwrites_original_method?
|
76
|
-
original_method_owner.send(:define_method, method_name, @original_method)
|
77
|
-
end
|
78
|
-
retain_original_visibility(original_method_owner)
|
79
|
-
end
|
80
|
-
|
81
57
|
def matches?(other)
|
82
58
|
return false unless other.class == self.class
|
83
59
|
(stubbee.object_id == other.stubbee.object_id) && (method_name == other.method_name)
|
@@ -100,18 +76,6 @@ module Mocha
|
|
100
76
|
@original_visibility = original_method_owner.__method_visibility__(method_name)
|
101
77
|
end
|
102
78
|
|
103
|
-
def stub_method_overwrites_original_method?
|
104
|
-
@original_method && @original_method.owner == original_method_owner
|
105
|
-
end
|
106
|
-
|
107
|
-
def remove_original_method_from_stubbee
|
108
|
-
original_method_owner.send(:remove_method, method_name)
|
109
|
-
end
|
110
|
-
|
111
|
-
def use_prepended_module_for_stub_method?
|
112
|
-
RUBY_V2_PLUS
|
113
|
-
end
|
114
|
-
|
115
79
|
def use_prepended_module_for_stub_method
|
116
80
|
@stub_method_owner = PrependedModule.new
|
117
81
|
original_method_owner.__send__ :prepend, @stub_method_owner
|
data/lib/mocha/test_unit.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
require 'mocha/ruby_version'
|
2
2
|
require 'mocha/integration/test_unit'
|
3
|
-
require 'mocha/deprecation'
|
4
3
|
|
5
4
|
unless Mocha::Integration::TestUnit.activate
|
6
|
-
|
7
|
-
"Test::Unit must be loaded *before* `require 'mocha/test_unit'`."
|
8
|
-
)
|
5
|
+
raise "Test::Unit must be loaded *before* `require 'mocha/test_unit'`."
|
9
6
|
end
|
data/lib/mocha/version.rb
CHANGED
data/mocha.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = 'mocha'
|
7
7
|
s.version = Mocha::VERSION
|
8
8
|
s.licenses = ['MIT', 'BSD-2-Clause']
|
9
|
-
s.required_ruby_version = '>= 1
|
9
|
+
s.required_ruby_version = '>= 2.1'
|
10
10
|
|
11
11
|
s.authors = ['James Mead']
|
12
12
|
s.description = 'Mocking and stubbing library with JMock/SchMock syntax, which allows mocking and stubbing of methods on real (non-mock) classes.'
|
@@ -21,4 +21,14 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.homepage = 'https://mocha.jamesmead.org'
|
22
22
|
s.require_paths = ['lib']
|
23
23
|
s.summary = 'Mocking and stubbing library'
|
24
|
+
s.metadata = {
|
25
|
+
'bug_tracker_uri' => 'https://github.com/freerange/mocha/issues',
|
26
|
+
'changelog_uri' => 'https://github.com/freerange/mocha/blob/main/RELEASE.md',
|
27
|
+
'documentation_uri' => 'https://mocha.jamesmead.org/',
|
28
|
+
'funding_uri' => 'https://github.com/sponsors/floehopper',
|
29
|
+
'homepage_uri' => s.homepage,
|
30
|
+
'source_code_uri' => 'https://github.com/freerange/mocha'
|
31
|
+
}
|
32
|
+
|
33
|
+
s.add_runtime_dependency 'ruby2_keywords', '>= 0.0.5'
|
24
34
|
end
|