mocha 1.16.1 → 2.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|