mocha 2.1.0 → 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 +6 -1
- data/.yardopts +1 -1
- data/Gemfile +6 -1
- data/README.md +13 -18
- data/RELEASE.md +199 -0
- data/Rakefile +9 -9
- data/lib/mocha/api.rb +25 -6
- data/lib/mocha/cardinality.rb +4 -0
- data/lib/mocha/configuration.rb +7 -0
- 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 +38 -6
- 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 +13 -2
- data/lib/mocha/integration/{mini_test → minitest}/adapter.rb +20 -5
- 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 +5 -0
- data/lib/mocha/minitest.rb +3 -3
- data/lib/mocha/mock.rb +37 -14
- data/lib/mocha/mockery.rb +13 -9
- data/lib/mocha/object_methods.rb +2 -2
- data/lib/mocha/parameter_matchers/base.rb +4 -9
- 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 +9 -10
- data/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb +3 -1
- data/lib/mocha/parameter_matchers/responds_with.rb +32 -5
- data/lib/mocha/parameters_matcher.rb +8 -4
- data/lib/mocha/ruby_version.rb +1 -0
- data/lib/mocha/version.rb +1 -1
- data/mocha.gemspec +9 -1
- metadata +15 -9
- data/lib/mocha/integration/mini_test.rb +0 -28
data/lib/mocha/expectation.rb
CHANGED
|
@@ -193,6 +193,11 @@ module Mocha
|
|
|
193
193
|
#
|
|
194
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.
|
|
195
195
|
#
|
|
196
|
+
# Alternatively a block argument can be passed to {#with} to implement custom parameter matching. The block receives the +*actual_parameters+ as its arguments and should return +true+ if they are acceptable or +false+ otherwise. See the example below where a method is expected to be called with a value divisible by 4.
|
|
197
|
+
# The block argument takes precedence over +expected_parameters_or_matchers+. The block may be called multiple times per invocation of the expected method and so it should be idempotent.
|
|
198
|
+
#
|
|
199
|
+
# Note that if {#with} is called multiple times on the same expectation, the last call takes precedence; other calls are ignored.
|
|
200
|
+
#
|
|
196
201
|
# 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
202
|
#
|
|
198
203
|
# 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.
|
|
@@ -202,10 +207,10 @@ module Mocha
|
|
|
202
207
|
# @see ParameterMatchers
|
|
203
208
|
# @see Configuration#strict_keyword_argument_matching=
|
|
204
209
|
#
|
|
205
|
-
# @param [
|
|
210
|
+
# @param [Array<Object,ParameterMatchers::Base>] expected_parameters_or_matchers expected parameter values or parameter matchers.
|
|
206
211
|
# @yield optional block specifying custom matching.
|
|
207
|
-
# @yieldparam [
|
|
208
|
-
# @yieldreturn [Boolean] +true+ if +actual_parameters+ are acceptable.
|
|
212
|
+
# @yieldparam [Array<Object>] actual_parameters parameters with which expected method was invoked.
|
|
213
|
+
# @yieldreturn [Boolean] +true+ if +actual_parameters+ are acceptable; +false+ otherwise.
|
|
209
214
|
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
210
215
|
#
|
|
211
216
|
# @example Expected method must be called with exact parameter values.
|
|
@@ -256,7 +261,7 @@ module Mocha
|
|
|
256
261
|
# example.foo('a', { bar: 'b' })
|
|
257
262
|
# # This now fails as expected
|
|
258
263
|
#
|
|
259
|
-
# @example
|
|
264
|
+
# @example Using a block argument to expect the method to be called with a value divisible by 4.
|
|
260
265
|
# object = mock()
|
|
261
266
|
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
|
|
262
267
|
# object.expected_method(16)
|
|
@@ -266,6 +271,22 @@ module Mocha
|
|
|
266
271
|
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
|
|
267
272
|
# object.expected_method(17)
|
|
268
273
|
# # => verify fails
|
|
274
|
+
#
|
|
275
|
+
# @example Extracting a custom matcher into an instance method on the test class.
|
|
276
|
+
# class MyTest < Minitest::Test
|
|
277
|
+
# def test_expected_method_is_called_with_a_value_divisible_by_4
|
|
278
|
+
# object = mock()
|
|
279
|
+
# object.expects(:expected_method).with(&method(:divisible_by_4))
|
|
280
|
+
# object.expected_method(16)
|
|
281
|
+
# # => verify succeeds
|
|
282
|
+
# end
|
|
283
|
+
#
|
|
284
|
+
# private
|
|
285
|
+
#
|
|
286
|
+
# def divisible_by_4(value)
|
|
287
|
+
# value % 4 == 0
|
|
288
|
+
# end
|
|
289
|
+
# end
|
|
269
290
|
def with(*expected_parameters_or_matchers, &matching_block)
|
|
270
291
|
@parameters_matcher = ParametersMatcher.new(expected_parameters_or_matchers, self, &matching_block)
|
|
271
292
|
self
|
|
@@ -632,14 +653,20 @@ module Mocha
|
|
|
632
653
|
@ordering_constraints.all?(&:allows_invocation_now?)
|
|
633
654
|
end
|
|
634
655
|
|
|
656
|
+
# @private
|
|
657
|
+
def ordering_constraints_not_allowing_invocation_now
|
|
658
|
+
@ordering_constraints.reject(&:allows_invocation_now?)
|
|
659
|
+
end
|
|
660
|
+
|
|
635
661
|
# @private
|
|
636
662
|
def matches_method?(method_name)
|
|
637
663
|
@method_matcher.match?(method_name)
|
|
638
664
|
end
|
|
639
665
|
|
|
640
666
|
# @private
|
|
641
|
-
def match?(invocation)
|
|
642
|
-
@method_matcher.match?(invocation.method_name) && @parameters_matcher.match?(invocation.arguments) && @block_matcher.match?(invocation.block)
|
|
667
|
+
def match?(invocation, ignoring_order: false)
|
|
668
|
+
order_independent_match = @method_matcher.match?(invocation.method_name) && @parameters_matcher.match?(invocation.arguments) && @block_matcher.match?(invocation.block)
|
|
669
|
+
ignoring_order ? order_independent_match : order_independent_match && in_correct_order?
|
|
643
670
|
end
|
|
644
671
|
|
|
645
672
|
# @private
|
|
@@ -647,6 +674,11 @@ module Mocha
|
|
|
647
674
|
@cardinality.invocations_allowed?
|
|
648
675
|
end
|
|
649
676
|
|
|
677
|
+
# @private
|
|
678
|
+
def invocations_never_allowed?
|
|
679
|
+
@cardinality.invocations_never_allowed?
|
|
680
|
+
end
|
|
681
|
+
|
|
650
682
|
# @private
|
|
651
683
|
def satisfied?
|
|
652
684
|
@cardinality.satisfied?
|
|
@@ -6,9 +6,9 @@ module Mocha
|
|
|
6
6
|
#
|
|
7
7
|
# This class should only be used by authors of test libraries and not by typical "users" of Mocha.
|
|
8
8
|
#
|
|
9
|
-
# For example, it is used by +Mocha::Integration::
|
|
9
|
+
# For example, it is used by +Mocha::Integration::Minitest::Adapter+ in order to have Mocha raise a +Minitest::Assertion+ which can then be sensibly handled by +Minitest::Unit::TestCase+.
|
|
10
10
|
#
|
|
11
|
-
# @see Mocha::Integration::
|
|
11
|
+
# @see Mocha::Integration::Minitest::Adapter
|
|
12
12
|
class ExpectationErrorFactory
|
|
13
13
|
class << self
|
|
14
14
|
# @!attribute exception_class
|
|
@@ -17,14 +17,18 @@ module Mocha
|
|
|
17
17
|
@expectations.any? { |expectation| expectation.matches_method?(method_name) }
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def match(invocation)
|
|
21
|
-
matching_expectations(invocation).first
|
|
20
|
+
def match(invocation, ignoring_order: false)
|
|
21
|
+
matching_expectations(invocation, ignoring_order: ignoring_order).first
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def match_allowing_invocation(invocation)
|
|
25
25
|
matching_expectations(invocation).detect(&:invocations_allowed?)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
def match_never_allowing_invocation(invocation)
|
|
29
|
+
matching_expectations(invocation).detect(&:invocations_never_allowed?)
|
|
30
|
+
end
|
|
31
|
+
|
|
28
32
|
def verified?(assertion_counter = nil)
|
|
29
33
|
@expectations.all? { |expectation| expectation.verified?(assertion_counter) }
|
|
30
34
|
end
|
|
@@ -49,10 +53,8 @@ module Mocha
|
|
|
49
53
|
self.class.new(to_a + other.to_a)
|
|
50
54
|
end
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def matching_expectations(invocation)
|
|
55
|
-
@expectations.select { |e| e.match?(invocation) }
|
|
56
|
+
def matching_expectations(invocation, ignoring_order: false)
|
|
57
|
+
@expectations.select { |e| e.match?(invocation, ignoring_order: ignoring_order) }
|
|
56
58
|
end
|
|
57
59
|
end
|
|
58
60
|
end
|
data/lib/mocha/hooks.rb
CHANGED
|
@@ -7,12 +7,12 @@ module Mocha
|
|
|
7
7
|
#
|
|
8
8
|
# This module is provided as part of the +Mocha::API+ module and is therefore part of the public API, but should only be used by authors of test libraries and not by typical "users" of Mocha.
|
|
9
9
|
#
|
|
10
|
-
# Integration with Test::Unit and
|
|
10
|
+
# Integration with Test::Unit and Minitest are provided as part of Mocha, because they are (or were once) part of the Ruby standard library. Integration with other test libraries is not provided as *part* of Mocha, but is supported by means of the methods in this module.
|
|
11
11
|
#
|
|
12
12
|
# See the code in the +Adapter+ modules for examples of how to use the methods in this module. +Mocha::ExpectationErrorFactory+ may be used if you want +Mocha+ to raise a different type of exception.
|
|
13
13
|
#
|
|
14
14
|
# @see Mocha::Integration::TestUnit::Adapter
|
|
15
|
-
# @see Mocha::Integration::
|
|
15
|
+
# @see Mocha::Integration::Minitest::Adapter
|
|
16
16
|
# @see Mocha::ExpectationErrorFactory
|
|
17
17
|
# @see Mocha::API
|
|
18
18
|
module Hooks
|
|
@@ -35,8 +35,14 @@ module Mocha
|
|
|
35
35
|
# Resets Mocha after a test (only for use by authors of test libraries).
|
|
36
36
|
#
|
|
37
37
|
# This method should be called after each individual test has finished (including after any "teardown" code).
|
|
38
|
-
def mocha_teardown
|
|
39
|
-
Mockery.teardown
|
|
38
|
+
def mocha_teardown(origin = mocha_test_name)
|
|
39
|
+
Mockery.teardown(origin)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Returns a string representing the unit test name, to be included in some Mocha
|
|
43
|
+
# to help track down potential bugs.
|
|
44
|
+
def mocha_test_name
|
|
45
|
+
nil
|
|
40
46
|
end
|
|
41
47
|
end
|
|
42
48
|
end
|
data/lib/mocha/inspect.rb
CHANGED
|
@@ -19,8 +19,19 @@ module Mocha
|
|
|
19
19
|
|
|
20
20
|
module HashMethods
|
|
21
21
|
def mocha_inspect
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
if Hash.ruby2_keywords_hash?(self)
|
|
23
|
+
collect do |key, value|
|
|
24
|
+
case key
|
|
25
|
+
when Symbol
|
|
26
|
+
"#{key}: #{value.mocha_inspect}"
|
|
27
|
+
else
|
|
28
|
+
"#{key.mocha_inspect} => #{value.mocha_inspect}"
|
|
29
|
+
end
|
|
30
|
+
end.join(', ')
|
|
31
|
+
else
|
|
32
|
+
unwrapped = collect { |key, value| "#{key.mocha_inspect} => #{value.mocha_inspect}" }.join(', ')
|
|
33
|
+
"{#{unwrapped}}"
|
|
34
|
+
end
|
|
24
35
|
end
|
|
25
36
|
end
|
|
26
37
|
|
|
@@ -4,21 +4,21 @@ require 'mocha/expectation_error_factory'
|
|
|
4
4
|
|
|
5
5
|
module Mocha
|
|
6
6
|
module Integration
|
|
7
|
-
module
|
|
8
|
-
# Integrates Mocha into recent versions of
|
|
7
|
+
module Minitest
|
|
8
|
+
# Integrates Mocha into recent versions of Minitest.
|
|
9
9
|
#
|
|
10
10
|
# See the source code for an example of how to integrate Mocha into a test library.
|
|
11
11
|
module Adapter
|
|
12
12
|
include Mocha::API
|
|
13
13
|
|
|
14
14
|
# @private
|
|
15
|
-
def self.applicable_to?(
|
|
16
|
-
Gem::Requirement.new('>= 3.3.0').satisfied_by?(
|
|
15
|
+
def self.applicable_to?(minitest_version)
|
|
16
|
+
Gem::Requirement.new('>= 3.3.0').satisfied_by?(minitest_version)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# @private
|
|
20
20
|
def self.description
|
|
21
|
-
'adapter for
|
|
21
|
+
'adapter for Minitest gem >= v3.3.0'
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
# @private
|
|
@@ -46,6 +46,21 @@ module Mocha
|
|
|
46
46
|
super
|
|
47
47
|
mocha_teardown
|
|
48
48
|
end
|
|
49
|
+
|
|
50
|
+
# @private
|
|
51
|
+
def mocha_test_name
|
|
52
|
+
if respond_to?(:name)
|
|
53
|
+
test_name = name
|
|
54
|
+
elsif respond_to?(:__name__) # Older minitest
|
|
55
|
+
test_name = __name__
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if test_name
|
|
59
|
+
"#{self.class.name}##{test_name}"
|
|
60
|
+
else
|
|
61
|
+
self.class.name
|
|
62
|
+
end
|
|
63
|
+
end
|
|
49
64
|
end
|
|
50
65
|
end
|
|
51
66
|
end
|
|
@@ -2,10 +2,10 @@ require 'mocha/expectation_error'
|
|
|
2
2
|
|
|
3
3
|
module Mocha
|
|
4
4
|
module Integration
|
|
5
|
-
module
|
|
5
|
+
module Minitest
|
|
6
6
|
def self.translate(exception)
|
|
7
7
|
return exception unless exception.is_a?(::Mocha::ExpectationError)
|
|
8
|
-
translated_exception = ::
|
|
8
|
+
translated_exception = ::Minitest::Assertion.new(exception.message)
|
|
9
9
|
translated_exception.set_backtrace(exception.backtrace)
|
|
10
10
|
translated_exception
|
|
11
11
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'mocha/debug'
|
|
2
|
+
require 'mocha/detection/minitest'
|
|
3
|
+
require 'mocha/integration/minitest/adapter'
|
|
4
|
+
|
|
5
|
+
module Mocha
|
|
6
|
+
module Integration
|
|
7
|
+
module Minitest
|
|
8
|
+
def self.activate
|
|
9
|
+
target = Detection::Minitest.testcase
|
|
10
|
+
return false unless target
|
|
11
|
+
|
|
12
|
+
minitest_version = Gem::Version.new(Detection::Minitest.version)
|
|
13
|
+
Debug.puts "Detected Minitest version: #{minitest_version}"
|
|
14
|
+
|
|
15
|
+
unless Minitest::Adapter.applicable_to?(minitest_version)
|
|
16
|
+
raise 'Versions of minitest earlier than v3.3.0 are not supported.'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
unless target < Minitest::Adapter
|
|
20
|
+
Debug.puts "Applying #{Minitest::Adapter.description}"
|
|
21
|
+
target.send(:include, Minitest::Adapter)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/mocha/minitest.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require 'mocha/ruby_version'
|
|
2
|
-
require 'mocha/integration/
|
|
2
|
+
require 'mocha/integration/minitest'
|
|
3
3
|
|
|
4
|
-
unless Mocha::Integration::
|
|
5
|
-
raise "
|
|
4
|
+
unless Mocha::Integration::Minitest.activate
|
|
5
|
+
raise "Minitest must be loaded *before* `require 'mocha/minitest'`."
|
|
6
6
|
end
|
data/lib/mocha/mock.rb
CHANGED
|
@@ -8,6 +8,7 @@ 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/deprecation'
|
|
11
12
|
|
|
12
13
|
module Mocha
|
|
13
14
|
# Traditional mock object.
|
|
@@ -34,6 +35,9 @@ module Mocha
|
|
|
34
35
|
# while an +expects(:foo).at_least_once+ expectation will always be matched
|
|
35
36
|
# against invocations.
|
|
36
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
|
+
#
|
|
37
41
|
# This scheme allows you to:
|
|
38
42
|
#
|
|
39
43
|
# - Set up default stubs in your the +setup+ method of your test class and
|
|
@@ -100,7 +104,7 @@ module Mocha
|
|
|
100
104
|
#
|
|
101
105
|
# @example Setup multiple expectations using +expected_methods_vs_return_values+.
|
|
102
106
|
# object = mock()
|
|
103
|
-
# object.expects(:
|
|
107
|
+
# object.expects(expected_method_one: :result_one, expected_method_two: :result_two)
|
|
104
108
|
#
|
|
105
109
|
# # is exactly equivalent to
|
|
106
110
|
#
|
|
@@ -114,6 +118,7 @@ module Mocha
|
|
|
114
118
|
method_name = args.shift
|
|
115
119
|
ensure_method_not_already_defined(method_name)
|
|
116
120
|
expectation = Expectation.new(self, method_name, backtrace)
|
|
121
|
+
expectation.in_sequence(@mockery.sequences.last) if @mockery.sequences.any?
|
|
117
122
|
expectation.returns(args.shift) unless args.empty?
|
|
118
123
|
@expectations.add(expectation)
|
|
119
124
|
end
|
|
@@ -138,7 +143,7 @@ module Mocha
|
|
|
138
143
|
#
|
|
139
144
|
# @example Setup multiple expectations using +stubbed_methods_vs_return_values+.
|
|
140
145
|
# object = mock()
|
|
141
|
-
# object.stubs(:
|
|
146
|
+
# object.stubs(stubbed_method_one: :result_one, stubbed_method_two: :result_two)
|
|
142
147
|
#
|
|
143
148
|
# # is exactly equivalent to
|
|
144
149
|
#
|
|
@@ -153,6 +158,7 @@ module Mocha
|
|
|
153
158
|
ensure_method_not_already_defined(method_name)
|
|
154
159
|
expectation = Expectation.new(self, method_name, backtrace)
|
|
155
160
|
expectation.at_least(0)
|
|
161
|
+
expectation.in_sequence(@mockery.sequences.last) if @mockery.sequences.any?
|
|
156
162
|
expectation.returns(args.shift) unless args.empty?
|
|
157
163
|
@expectations.add(expectation)
|
|
158
164
|
end
|
|
@@ -309,22 +315,33 @@ module Mocha
|
|
|
309
315
|
end
|
|
310
316
|
|
|
311
317
|
# @private
|
|
312
|
-
# rubocop:disable Style/MethodMissingSuper
|
|
313
|
-
def method_missing(symbol, *arguments, &block)
|
|
318
|
+
def method_missing(symbol, *arguments, &block) # rubocop:disable Style/MethodMissingSuper
|
|
314
319
|
handle_method_call(symbol, arguments, block)
|
|
315
320
|
end
|
|
316
321
|
ruby2_keywords(:method_missing)
|
|
317
|
-
# rubocop:enable Style/MethodMissingSuper
|
|
318
322
|
|
|
319
323
|
# @private
|
|
320
|
-
def handle_method_call(symbol, arguments, block)
|
|
324
|
+
def handle_method_call(symbol, arguments, block) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
321
325
|
check_expiry
|
|
322
326
|
check_responder_responds_to(symbol)
|
|
323
327
|
invocation = Invocation.new(self, symbol, arguments, block)
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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)
|
|
328
345
|
end
|
|
329
346
|
end
|
|
330
347
|
|
|
@@ -343,8 +360,8 @@ module Mocha
|
|
|
343
360
|
end
|
|
344
361
|
|
|
345
362
|
# @private
|
|
346
|
-
def __expire__
|
|
347
|
-
@expired = true
|
|
363
|
+
def __expire__(origin)
|
|
364
|
+
@expired = origin || true
|
|
348
365
|
end
|
|
349
366
|
|
|
350
367
|
# @private
|
|
@@ -373,7 +390,11 @@ module Mocha
|
|
|
373
390
|
if @unexpected_invocation.nil?
|
|
374
391
|
@unexpected_invocation = invocation
|
|
375
392
|
matching_expectation.invoke(invocation) if matching_expectation
|
|
376
|
-
|
|
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}"
|
|
377
398
|
else
|
|
378
399
|
message = @unexpected_invocation.short_call_description
|
|
379
400
|
end
|
|
@@ -389,8 +410,10 @@ module Mocha
|
|
|
389
410
|
def check_expiry
|
|
390
411
|
return unless @expired
|
|
391
412
|
|
|
413
|
+
origin = @expired == true ? 'one test' : @expired
|
|
414
|
+
|
|
392
415
|
sentences = [
|
|
393
|
-
"#{mocha_inspect} was instantiated in
|
|
416
|
+
"#{mocha_inspect} was instantiated in #{origin} but it is receiving invocations within another test.",
|
|
394
417
|
'This can lead to unintended interactions between tests and hence unexpected test failures.',
|
|
395
418
|
'Ensure that every test correctly cleans up any state that it introduces.'
|
|
396
419
|
]
|
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/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(_expectation = nil)
|
|
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))
|
|
@@ -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,3 +1,4 @@
|
|
|
1
|
+
require 'mocha/parameter_matchers/base'
|
|
1
2
|
require 'mocha/parameter_matchers/equals'
|
|
2
3
|
require 'mocha/parameter_matchers/positional_or_keyword_hash'
|
|
3
4
|
|
|
@@ -6,8 +7,14 @@ module Mocha
|
|
|
6
7
|
# @private
|
|
7
8
|
module InstanceMethods
|
|
8
9
|
# @private
|
|
9
|
-
def to_matcher(
|
|
10
|
-
|
|
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
|
|
11
18
|
end
|
|
12
19
|
end
|
|
13
20
|
end
|
|
@@ -17,11 +24,3 @@ end
|
|
|
17
24
|
class Object
|
|
18
25
|
include Mocha::ParameterMatchers::InstanceMethods
|
|
19
26
|
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
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'mocha/configuration'
|
|
2
2
|
require 'mocha/deprecation'
|
|
3
3
|
require 'mocha/parameter_matchers/base'
|
|
4
|
+
require 'mocha/parameter_matchers/has_entries'
|
|
4
5
|
|
|
5
6
|
module Mocha
|
|
6
7
|
module ParameterMatchers
|
|
@@ -13,7 +14,8 @@ module Mocha
|
|
|
13
14
|
|
|
14
15
|
def matches?(available_parameters)
|
|
15
16
|
parameter, is_last_parameter = extract_parameter(available_parameters)
|
|
16
|
-
|
|
17
|
+
|
|
18
|
+
return false unless HasEntries.new(@value, exact: true).matches?([parameter])
|
|
17
19
|
|
|
18
20
|
if is_last_parameter && !same_type_of_hash?(parameter, @value)
|
|
19
21
|
return false if Mocha.configuration.strict_keyword_argument_matching?
|