mocha 0.5.6 → 3.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 +7 -0
- data/.gemtest +0 -0
- data/.github/FUNDING.yml +1 -0
- data/.rubocop.yml +92 -0
- data/.rubocop_todo.yml +39 -0
- data/.yardopts +25 -0
- data/CONTRIBUTING.md +7 -0
- data/COPYING.md +3 -0
- data/Gemfile +17 -0
- data/{MIT-LICENSE → MIT-LICENSE.md} +2 -2
- data/README.md +361 -0
- data/RELEASE.md +1247 -0
- data/Rakefile +165 -123
- data/gemfiles/Gemfile.minitest.latest +8 -0
- data/gemfiles/Gemfile.rubocop +9 -0
- data/gemfiles/Gemfile.test-unit.latest +8 -0
- data/lib/mocha/any_instance_method.rb +12 -26
- data/lib/mocha/any_instance_receiver.rb +20 -0
- data/lib/mocha/api.rb +213 -0
- data/lib/mocha/argument_iterator.rb +17 -0
- data/lib/mocha/backtrace_filter.rb +25 -0
- data/lib/mocha/block_matchers.rb +33 -0
- data/lib/mocha/cardinality.rb +110 -0
- data/lib/mocha/central.rb +33 -22
- data/lib/mocha/change_state_side_effect.rb +17 -0
- data/lib/mocha/class_methods.rb +67 -0
- data/lib/mocha/configuration.rb +338 -0
- data/lib/mocha/default_name.rb +15 -0
- data/lib/mocha/default_receiver.rb +13 -0
- data/lib/mocha/deprecation.rb +6 -15
- data/lib/mocha/detection/minitest.rb +25 -0
- data/lib/mocha/detection/test_unit.rb +30 -0
- data/lib/mocha/error_with_filtered_backtrace.rb +15 -0
- data/lib/mocha/exception_raiser.rb +11 -10
- data/lib/mocha/expectation.rb +562 -171
- data/lib/mocha/expectation_error.rb +9 -14
- data/lib/mocha/expectation_error_factory.rb +37 -0
- data/lib/mocha/expectation_list.rb +30 -14
- data/lib/mocha/hooks.rb +55 -0
- data/lib/mocha/ignoring_warning.rb +20 -0
- data/lib/mocha/impersonating_any_instance_name.rb +13 -0
- data/lib/mocha/impersonating_name.rb +13 -0
- data/lib/mocha/in_state_ordering_constraint.rb +17 -0
- data/lib/mocha/inspect.rb +54 -30
- data/lib/mocha/instance_method.rb +17 -4
- data/lib/mocha/integration/assertion_counter.rb +15 -0
- data/lib/mocha/integration/minitest/adapter.rb +71 -0
- data/lib/mocha/integration/minitest.rb +29 -0
- data/lib/mocha/integration/monkey_patcher.rb +26 -0
- data/lib/mocha/integration/test_unit/adapter.rb +61 -0
- data/lib/mocha/integration/test_unit.rb +29 -0
- data/lib/mocha/integration.rb +5 -0
- data/lib/mocha/invocation.rb +76 -0
- data/lib/mocha/logger.rb +26 -0
- data/lib/mocha/macos_version.rb +7 -0
- data/lib/mocha/method_matcher.rb +8 -10
- data/lib/mocha/minitest.rb +7 -0
- data/lib/mocha/mock.rb +333 -108
- data/lib/mocha/mockery.rb +192 -0
- data/lib/mocha/name.rb +13 -0
- data/lib/mocha/not_initialized_error.rb +9 -0
- data/lib/mocha/object_methods.rb +183 -0
- data/lib/mocha/object_receiver.rb +20 -0
- data/lib/mocha/parameter_matchers/all_of.rb +38 -28
- data/lib/mocha/parameter_matchers/any_of.rb +44 -33
- data/lib/mocha/parameter_matchers/any_parameters.rb +33 -26
- data/lib/mocha/parameter_matchers/anything.rb +31 -22
- data/lib/mocha/parameter_matchers/base_methods.rb +64 -0
- data/lib/mocha/parameter_matchers/equals.rb +36 -25
- data/lib/mocha/parameter_matchers/equivalent_uri.rb +65 -0
- data/lib/mocha/parameter_matchers/has_entries.rb +48 -29
- data/lib/mocha/parameter_matchers/has_entry.rb +90 -42
- data/lib/mocha/parameter_matchers/has_key.rb +39 -26
- data/lib/mocha/parameter_matchers/has_keys.rb +59 -0
- data/lib/mocha/parameter_matchers/has_value.rb +39 -26
- data/lib/mocha/parameter_matchers/includes.rb +88 -23
- data/lib/mocha/parameter_matchers/instance_methods.rb +28 -0
- data/lib/mocha/parameter_matchers/instance_of.rb +37 -26
- data/lib/mocha/parameter_matchers/is_a.rb +38 -26
- data/lib/mocha/parameter_matchers/kind_of.rb +39 -26
- data/lib/mocha/parameter_matchers/not.rb +37 -26
- data/lib/mocha/parameter_matchers/optionally.rb +52 -17
- data/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb +91 -0
- data/lib/mocha/parameter_matchers/regexp_matches.rb +37 -25
- data/lib/mocha/parameter_matchers/responds_with.rb +82 -0
- data/lib/mocha/parameter_matchers/yaml_equivalent.rb +55 -0
- data/lib/mocha/parameter_matchers.rb +12 -5
- data/lib/mocha/parameters_matcher.rb +28 -19
- data/lib/mocha/raised_exception.rb +13 -0
- data/lib/mocha/return_values.rb +13 -18
- data/lib/mocha/ruby_version.rb +7 -0
- data/lib/mocha/sequence.rb +23 -17
- data/lib/mocha/single_return_value.rb +8 -18
- data/lib/mocha/state_machine.rb +95 -0
- data/lib/mocha/stubbed_method.rb +96 -0
- data/lib/mocha/stubbing_error.rb +10 -0
- data/lib/mocha/test_unit.rb +7 -0
- data/lib/mocha/thrower.rb +15 -0
- data/lib/mocha/thrown_object.rb +14 -0
- data/lib/mocha/version.rb +5 -0
- data/lib/mocha/yield_parameters.rb +12 -20
- data/lib/mocha.rb +19 -17
- data/mocha.gemspec +40 -0
- metadata +129 -145
- data/COPYING +0 -3
- data/README +0 -35
- data/RELEASE +0 -188
- data/examples/misc.rb +0 -44
- data/examples/mocha.rb +0 -26
- data/examples/stubba.rb +0 -65
- data/lib/mocha/auto_verify.rb +0 -118
- data/lib/mocha/class_method.rb +0 -66
- data/lib/mocha/infinite_range.rb +0 -25
- data/lib/mocha/is_a.rb +0 -9
- data/lib/mocha/metaclass.rb +0 -7
- data/lib/mocha/missing_expectation.rb +0 -17
- data/lib/mocha/multiple_yields.rb +0 -20
- data/lib/mocha/no_yields.rb +0 -11
- data/lib/mocha/object.rb +0 -110
- data/lib/mocha/parameter_matchers/base.rb +0 -15
- data/lib/mocha/parameter_matchers/object.rb +0 -9
- data/lib/mocha/pretty_parameters.rb +0 -28
- data/lib/mocha/setup_and_teardown.rb +0 -23
- data/lib/mocha/single_yield.rb +0 -18
- data/lib/mocha/standalone.rb +0 -32
- data/lib/mocha/stub.rb +0 -18
- data/lib/mocha/test_case_adapter.rb +0 -49
- data/lib/mocha_standalone.rb +0 -2
- data/lib/stubba.rb +0 -2
- data/test/acceptance/expected_invocation_count_acceptance_test.rb +0 -187
- data/test/acceptance/mocha_acceptance_test.rb +0 -98
- data/test/acceptance/mock_with_initializer_block_acceptance_test.rb +0 -44
- data/test/acceptance/mocked_methods_dispatch_acceptance_test.rb +0 -71
- data/test/acceptance/optional_parameters_acceptance_test.rb +0 -63
- data/test/acceptance/parameter_matcher_acceptance_test.rb +0 -117
- data/test/acceptance/partial_mocks_acceptance_test.rb +0 -40
- data/test/acceptance/sequence_acceptance_test.rb +0 -179
- data/test/acceptance/standalone_acceptance_test.rb +0 -131
- data/test/acceptance/stubba_acceptance_test.rb +0 -102
- data/test/active_record_test_case.rb +0 -36
- data/test/deprecation_disabler.rb +0 -15
- data/test/execution_point.rb +0 -34
- data/test/integration/mocha_test_result_integration_test.rb +0 -105
- data/test/integration/stubba_integration_test.rb +0 -89
- data/test/integration/stubba_test_result_integration_test.rb +0 -85
- data/test/method_definer.rb +0 -18
- data/test/test_helper.rb +0 -12
- data/test/test_runner.rb +0 -31
- data/test/unit/any_instance_method_test.rb +0 -126
- data/test/unit/array_inspect_test.rb +0 -16
- data/test/unit/auto_verify_test.rb +0 -129
- data/test/unit/central_test.rb +0 -124
- data/test/unit/class_method_test.rb +0 -200
- data/test/unit/date_time_inspect_test.rb +0 -21
- data/test/unit/expectation_error_test.rb +0 -24
- data/test/unit/expectation_list_test.rb +0 -75
- data/test/unit/expectation_raiser_test.rb +0 -28
- data/test/unit/expectation_test.rb +0 -483
- data/test/unit/hash_inspect_test.rb +0 -16
- data/test/unit/infinite_range_test.rb +0 -53
- data/test/unit/metaclass_test.rb +0 -22
- data/test/unit/method_matcher_test.rb +0 -23
- data/test/unit/missing_expectation_test.rb +0 -42
- data/test/unit/mock_test.rb +0 -323
- data/test/unit/multiple_yields_test.rb +0 -18
- data/test/unit/no_yield_test.rb +0 -18
- data/test/unit/object_inspect_test.rb +0 -37
- data/test/unit/object_test.rb +0 -165
- data/test/unit/parameter_matchers/all_of_test.rb +0 -26
- data/test/unit/parameter_matchers/any_of_test.rb +0 -26
- data/test/unit/parameter_matchers/anything_test.rb +0 -21
- data/test/unit/parameter_matchers/has_entries_test.rb +0 -30
- data/test/unit/parameter_matchers/has_entry_test.rb +0 -40
- data/test/unit/parameter_matchers/has_key_test.rb +0 -25
- data/test/unit/parameter_matchers/has_value_test.rb +0 -25
- data/test/unit/parameter_matchers/includes_test.rb +0 -25
- data/test/unit/parameter_matchers/instance_of_test.rb +0 -25
- data/test/unit/parameter_matchers/is_a_test.rb +0 -25
- data/test/unit/parameter_matchers/kind_of_test.rb +0 -25
- data/test/unit/parameter_matchers/not_test.rb +0 -26
- data/test/unit/parameter_matchers/regexp_matches_test.rb +0 -25
- data/test/unit/parameter_matchers/stub_matcher.rb +0 -23
- data/test/unit/parameters_matcher_test.rb +0 -121
- data/test/unit/return_values_test.rb +0 -63
- data/test/unit/sequence_test.rb +0 -104
- data/test/unit/setup_and_teardown_test.rb +0 -76
- data/test/unit/single_return_value_test.rb +0 -33
- data/test/unit/single_yield_test.rb +0 -18
- data/test/unit/string_inspect_test.rb +0 -11
- data/test/unit/stub_test.rb +0 -24
- data/test/unit/yield_parameters_test.rb +0 -93
data/lib/mocha/expectation.rb
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ruby2_keywords'
|
|
2
4
|
require 'mocha/method_matcher'
|
|
3
5
|
require 'mocha/parameters_matcher'
|
|
4
6
|
require 'mocha/expectation_error'
|
|
5
7
|
require 'mocha/return_values'
|
|
6
8
|
require 'mocha/exception_raiser'
|
|
9
|
+
require 'mocha/thrower'
|
|
7
10
|
require 'mocha/yield_parameters'
|
|
8
|
-
require 'mocha/
|
|
11
|
+
require 'mocha/in_state_ordering_constraint'
|
|
12
|
+
require 'mocha/change_state_side_effect'
|
|
13
|
+
require 'mocha/cardinality'
|
|
14
|
+
require 'mocha/configuration'
|
|
15
|
+
require 'mocha/block_matchers'
|
|
16
|
+
require 'mocha/backtrace_filter'
|
|
9
17
|
|
|
10
|
-
module Mocha
|
|
11
|
-
|
|
12
|
-
# Methods on expectations returned from Mock#expects, Mock#stubs, Object#expects and Object#stubs.
|
|
18
|
+
module Mocha
|
|
19
|
+
# Methods on expectations returned from {Mock#expects}, {Mock#stubs}, {ObjectMethods#expects} and {ObjectMethods#stubs}.
|
|
13
20
|
class Expectation
|
|
14
|
-
|
|
15
|
-
# :call-seq: times(range) -> expectation
|
|
21
|
+
# Modifies expectation so that the number of invocations of the expected method must be within a specified range or exactly equal to a specified number.
|
|
16
22
|
#
|
|
17
|
-
#
|
|
23
|
+
# @param [Range,Integer] range_or_number specifies the allowable range for the number of expected invocations or the specified number of expected invocations.
|
|
24
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
18
25
|
#
|
|
19
|
-
#
|
|
26
|
+
# @example Specifying an exact number of expected invocations.
|
|
20
27
|
# object = mock()
|
|
21
28
|
# object.expects(:expected_method).times(3)
|
|
22
29
|
# 3.times { object.expected_method }
|
|
@@ -27,6 +34,7 @@ module Mocha # :nodoc:
|
|
|
27
34
|
# 2.times { object.expected_method }
|
|
28
35
|
# # => verify fails
|
|
29
36
|
#
|
|
37
|
+
# @example Specifying a range for the number of expected invocations.
|
|
30
38
|
# object = mock()
|
|
31
39
|
# object.expects(:expected_method).times(2..4)
|
|
32
40
|
# 3.times { object.expected_method }
|
|
@@ -36,15 +44,73 @@ module Mocha # :nodoc:
|
|
|
36
44
|
# object.expects(:expected_method).times(2..4)
|
|
37
45
|
# object.expected_method
|
|
38
46
|
# # => verify fails
|
|
39
|
-
def times(
|
|
40
|
-
@
|
|
47
|
+
def times(range_or_number)
|
|
48
|
+
@cardinality.times(range_or_number)
|
|
41
49
|
self
|
|
42
50
|
end
|
|
43
|
-
|
|
44
|
-
#
|
|
51
|
+
|
|
52
|
+
# Modifies expectation so that the expected method must be called exactly three times. This is equivalent to calling {#times} with an argument of +3+.
|
|
53
|
+
#
|
|
54
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
55
|
+
#
|
|
56
|
+
# @example Expected method must be invoked exactly three times.
|
|
57
|
+
# object = mock()
|
|
58
|
+
# object.expects(:expected_method).thrice
|
|
59
|
+
# object.expected_method
|
|
60
|
+
# object.expected_method
|
|
61
|
+
# object.expected_method
|
|
62
|
+
# # => verify succeeds
|
|
63
|
+
#
|
|
64
|
+
# object = mock()
|
|
65
|
+
# object.expects(:expected_method).thrice
|
|
66
|
+
# object.expected_method
|
|
67
|
+
# object.expected_method
|
|
68
|
+
# object.expected_method
|
|
69
|
+
# object.expected_method # => unexpected invocation
|
|
70
|
+
#
|
|
71
|
+
# object = mock()
|
|
72
|
+
# object.expects(:expected_method).thrice
|
|
73
|
+
# object.expected_method
|
|
74
|
+
# object.expected_method
|
|
75
|
+
# # => verify fails
|
|
76
|
+
def thrice
|
|
77
|
+
@cardinality.exactly(3)
|
|
78
|
+
self
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Modifies expectation so that the expected method must be called exactly twice. This is equivalent to calling {#times} with an argument of +2+.
|
|
82
|
+
#
|
|
83
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
84
|
+
#
|
|
85
|
+
# @example Expected method must be invoked exactly twice.
|
|
86
|
+
# object = mock()
|
|
87
|
+
# object.expects(:expected_method).twice
|
|
88
|
+
# object.expected_method
|
|
89
|
+
# object.expected_method
|
|
90
|
+
# # => verify succeeds
|
|
91
|
+
#
|
|
92
|
+
# object = mock()
|
|
93
|
+
# object.expects(:expected_method).twice
|
|
94
|
+
# object.expected_method
|
|
95
|
+
# object.expected_method
|
|
96
|
+
# object.expected_method # => unexpected invocation
|
|
97
|
+
#
|
|
98
|
+
# object = mock()
|
|
99
|
+
# object.expects(:expected_method).twice
|
|
100
|
+
# object.expected_method
|
|
101
|
+
# # => verify fails
|
|
102
|
+
def twice
|
|
103
|
+
@cardinality.exactly(2)
|
|
104
|
+
self
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Modifies expectation so that the expected method must be called exactly once. This is equivalent to calling {#times} with an argument of +1+.
|
|
45
108
|
#
|
|
46
|
-
# Modifies expectation so that the expected method must be called exactly once.
|
|
47
109
|
# Note that this is the default behaviour for an expectation, but you may wish to use it for clarity/emphasis.
|
|
110
|
+
#
|
|
111
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
112
|
+
#
|
|
113
|
+
# @example Expected method must be invoked exactly once.
|
|
48
114
|
# object = mock()
|
|
49
115
|
# object.expects(:expected_method).once
|
|
50
116
|
# object.expected_method
|
|
@@ -53,37 +119,39 @@ module Mocha # :nodoc:
|
|
|
53
119
|
# object = mock()
|
|
54
120
|
# object.expects(:expected_method).once
|
|
55
121
|
# object.expected_method
|
|
56
|
-
# object.expected_method
|
|
57
|
-
# # => verify fails
|
|
122
|
+
# object.expected_method # => unexpected invocation
|
|
58
123
|
#
|
|
59
124
|
# object = mock()
|
|
60
125
|
# object.expects(:expected_method).once
|
|
61
126
|
# # => verify fails
|
|
62
|
-
def once
|
|
63
|
-
|
|
127
|
+
def once
|
|
128
|
+
@cardinality.exactly(1)
|
|
64
129
|
self
|
|
65
130
|
end
|
|
66
|
-
|
|
67
|
-
# :call-seq: never() -> expectation
|
|
68
|
-
#
|
|
131
|
+
|
|
69
132
|
# Modifies expectation so that the expected method must never be called.
|
|
133
|
+
#
|
|
134
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
135
|
+
#
|
|
136
|
+
# @example Expected method must never be called.
|
|
70
137
|
# object = mock()
|
|
71
138
|
# object.expects(:expected_method).never
|
|
72
|
-
# object.expected_method
|
|
73
|
-
# # => verify fails
|
|
139
|
+
# object.expected_method # => unexpected invocation
|
|
74
140
|
#
|
|
75
141
|
# object = mock()
|
|
76
142
|
# object.expects(:expected_method).never
|
|
77
|
-
# object.expected_method
|
|
78
143
|
# # => verify succeeds
|
|
79
144
|
def never
|
|
80
|
-
|
|
145
|
+
@cardinality.exactly(0)
|
|
81
146
|
self
|
|
82
147
|
end
|
|
83
|
-
|
|
84
|
-
# :call-seq: at_least(minimum_number_of_times) -> expectation
|
|
85
|
-
#
|
|
148
|
+
|
|
86
149
|
# Modifies expectation so that the expected method must be called at least a +minimum_number_of_times+.
|
|
150
|
+
#
|
|
151
|
+
# @param [Integer] minimum_number_of_times minimum number of expected invocations.
|
|
152
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
153
|
+
#
|
|
154
|
+
# @example Expected method must be called at least twice.
|
|
87
155
|
# object = mock()
|
|
88
156
|
# object.expects(:expected_method).at_least(2)
|
|
89
157
|
# 3.times { object.expected_method }
|
|
@@ -94,13 +162,15 @@ module Mocha # :nodoc:
|
|
|
94
162
|
# object.expected_method
|
|
95
163
|
# # => verify fails
|
|
96
164
|
def at_least(minimum_number_of_times)
|
|
97
|
-
|
|
165
|
+
@cardinality.at_least(minimum_number_of_times)
|
|
98
166
|
self
|
|
99
167
|
end
|
|
100
|
-
|
|
101
|
-
#
|
|
168
|
+
|
|
169
|
+
# Modifies expectation so that the expected method must be called at least once. This is equivalent to calling {#at_least} with an argument of +1+.
|
|
102
170
|
#
|
|
103
|
-
#
|
|
171
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
172
|
+
#
|
|
173
|
+
# @example Expected method must be called at least once.
|
|
104
174
|
# object = mock()
|
|
105
175
|
# object.expects(:expected_method).at_least_once
|
|
106
176
|
# object.expected_method
|
|
@@ -109,14 +179,16 @@ module Mocha # :nodoc:
|
|
|
109
179
|
# object = mock()
|
|
110
180
|
# object.expects(:expected_method).at_least_once
|
|
111
181
|
# # => verify fails
|
|
112
|
-
def at_least_once
|
|
182
|
+
def at_least_once
|
|
113
183
|
at_least(1)
|
|
114
|
-
self
|
|
115
184
|
end
|
|
116
|
-
|
|
117
|
-
# :call-seq: at_most(maximum_number_of_times) -> expectation
|
|
118
|
-
#
|
|
185
|
+
|
|
119
186
|
# Modifies expectation so that the expected method must be called at most a +maximum_number_of_times+.
|
|
187
|
+
#
|
|
188
|
+
# @param [Integer] maximum_number_of_times maximum number of expected invocations.
|
|
189
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
190
|
+
#
|
|
191
|
+
# @example Expected method must be called at most twice.
|
|
120
192
|
# object = mock()
|
|
121
193
|
# object.expects(:expected_method).at_most(2)
|
|
122
194
|
# 2.times { object.expected_method }
|
|
@@ -124,16 +196,17 @@ module Mocha # :nodoc:
|
|
|
124
196
|
#
|
|
125
197
|
# object = mock()
|
|
126
198
|
# object.expects(:expected_method).at_most(2)
|
|
127
|
-
# 3.times { object.expected_method }
|
|
128
|
-
# # => verify fails
|
|
199
|
+
# 3.times { object.expected_method } # => unexpected invocation
|
|
129
200
|
def at_most(maximum_number_of_times)
|
|
130
|
-
|
|
201
|
+
@cardinality.at_most(maximum_number_of_times)
|
|
131
202
|
self
|
|
132
203
|
end
|
|
133
|
-
|
|
134
|
-
#
|
|
204
|
+
|
|
205
|
+
# Modifies expectation so that the expected method must be called at most once. This is equivalent to calling {#at_most} with an argument of +1+.
|
|
206
|
+
#
|
|
207
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
135
208
|
#
|
|
136
|
-
#
|
|
209
|
+
# @example Expected method must be called at most once.
|
|
137
210
|
# object = mock()
|
|
138
211
|
# object.expects(:expected_method).at_most_once
|
|
139
212
|
# object.expected_method
|
|
@@ -141,16 +214,36 @@ module Mocha # :nodoc:
|
|
|
141
214
|
#
|
|
142
215
|
# object = mock()
|
|
143
216
|
# object.expects(:expected_method).at_most_once
|
|
144
|
-
# 2.times { object.expected_method }
|
|
145
|
-
|
|
146
|
-
def at_most_once()
|
|
217
|
+
# 2.times { object.expected_method } # => unexpected invocation
|
|
218
|
+
def at_most_once
|
|
147
219
|
at_most(1)
|
|
148
|
-
self
|
|
149
220
|
end
|
|
150
|
-
|
|
151
|
-
#
|
|
221
|
+
|
|
222
|
+
# Modifies expectation so that the expected method must be called with +expected_parameters_or_matchers+.
|
|
223
|
+
#
|
|
224
|
+
# May be used with Ruby literals or variables for exact matching or with parameter matchers for less-specific matching, e.g. {ParameterMatchers::Methods#includes}, {ParameterMatchers::Methods#has_key}, etc. See {ParameterMatchers} for a list of all available parameter matchers.
|
|
225
|
+
#
|
|
226
|
+
# 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.
|
|
227
|
+
# 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.
|
|
228
|
+
#
|
|
229
|
+
# Note that if {#with} is called multiple times on the same expectation, the last call takes precedence; other calls are ignored.
|
|
230
|
+
#
|
|
231
|
+
# 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.
|
|
152
232
|
#
|
|
153
|
-
#
|
|
233
|
+
# When {Configuration#strict_keyword_argument_matching=} is set to +false+ (which is the default in Ruby v2.7), 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.
|
|
234
|
+
#
|
|
235
|
+
# When {Configuration#strict_keyword_argument_matching=} is set to +true+ (which is the default in Ruby >= v3.0), an actual positional +Hash+ will not match an expected set of keyword arguments; and vice versa, an actual set of keyword arguments will not match an expected positional +Hash+, i.e. the parameter matching is stricter.
|
|
236
|
+
#
|
|
237
|
+
# @see ParameterMatchers
|
|
238
|
+
# @see Configuration#strict_keyword_argument_matching=
|
|
239
|
+
#
|
|
240
|
+
# @param [Array<Object,ParameterMatchers::BaseMethods>] expected_parameters_or_matchers expected parameter values or parameter matchers.
|
|
241
|
+
# @yield optional block specifying custom matching.
|
|
242
|
+
# @yieldparam [Array<Object>] actual_parameters parameters with which expected method was invoked.
|
|
243
|
+
# @yieldreturn [Boolean] +true+ if +actual_parameters+ are acceptable; +false+ otherwise.
|
|
244
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
245
|
+
#
|
|
246
|
+
# @example Expected method must be called with exact parameter values.
|
|
154
247
|
# object = mock()
|
|
155
248
|
# object.expects(:expected_method).with(:param1, :param2)
|
|
156
249
|
# object.expected_method(:param1, :param2)
|
|
@@ -160,10 +253,49 @@ module Mocha # :nodoc:
|
|
|
160
253
|
# object.expects(:expected_method).with(:param1, :param2)
|
|
161
254
|
# object.expected_method(:param3)
|
|
162
255
|
# # => verify fails
|
|
163
|
-
# May be used with parameter matchers in Mocha::ParameterMatchers.
|
|
164
256
|
#
|
|
165
|
-
#
|
|
166
|
-
#
|
|
257
|
+
# @example Expected method must be called with parameters matching parameter matchers.
|
|
258
|
+
# object = mock()
|
|
259
|
+
# object.expects(:expected_method).with(includes('string2'), anything)
|
|
260
|
+
# object.expected_method(['string1', 'string2'], 'any-old-value')
|
|
261
|
+
# # => verify succeeds
|
|
262
|
+
#
|
|
263
|
+
# object = mock()
|
|
264
|
+
# object.expects(:expected_method).with(includes('string2'), anything)
|
|
265
|
+
# object.expected_method(['string1'], 'any-old-value')
|
|
266
|
+
# # => verify fails
|
|
267
|
+
#
|
|
268
|
+
# @example Loose keyword argument matching (default in Ruby v2.7).
|
|
269
|
+
#
|
|
270
|
+
# Mocha.configure do |c|
|
|
271
|
+
# c.strict_keyword_argument_matching = false
|
|
272
|
+
# end
|
|
273
|
+
#
|
|
274
|
+
# class Example
|
|
275
|
+
# def foo(a, bar:); end
|
|
276
|
+
# end
|
|
277
|
+
#
|
|
278
|
+
# example = Example.new
|
|
279
|
+
# example.expects(:foo).with('a', bar: 'b')
|
|
280
|
+
# example.foo('a', { bar: 'b' })
|
|
281
|
+
# # This passes the test, but would result in an ArgumentError in practice
|
|
282
|
+
#
|
|
283
|
+
# @example Strict keyword argument matching (default in Ruby >= 3.0).
|
|
284
|
+
#
|
|
285
|
+
# Mocha.configure do |c|
|
|
286
|
+
# c.strict_keyword_argument_matching = true
|
|
287
|
+
# end
|
|
288
|
+
#
|
|
289
|
+
# class Example
|
|
290
|
+
# def foo(a, bar:); end
|
|
291
|
+
# end
|
|
292
|
+
#
|
|
293
|
+
# example = Example.new
|
|
294
|
+
# example.expects(:foo).with('a', bar: 'b')
|
|
295
|
+
# example.foo('a', { bar: 'b' })
|
|
296
|
+
# # This now fails as expected
|
|
297
|
+
#
|
|
298
|
+
# @example Using a block argument to expect the method to be called with a value divisible by 4.
|
|
167
299
|
# object = mock()
|
|
168
300
|
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
|
|
169
301
|
# object.expected_method(16)
|
|
@@ -173,108 +305,211 @@ module Mocha # :nodoc:
|
|
|
173
305
|
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
|
|
174
306
|
# object.expected_method(17)
|
|
175
307
|
# # => verify fails
|
|
176
|
-
|
|
177
|
-
|
|
308
|
+
#
|
|
309
|
+
# @example Extracting a custom matcher into an instance method on the test class.
|
|
310
|
+
# class MyTest < Minitest::Test
|
|
311
|
+
# def test_expected_method_is_called_with_a_value_divisible_by_4
|
|
312
|
+
# object = mock()
|
|
313
|
+
# object.expects(:expected_method).with(&method(:divisible_by_4))
|
|
314
|
+
# object.expected_method(16)
|
|
315
|
+
# # => verify succeeds
|
|
316
|
+
# end
|
|
317
|
+
#
|
|
318
|
+
# private
|
|
319
|
+
#
|
|
320
|
+
# def divisible_by_4(value)
|
|
321
|
+
# value % 4 == 0
|
|
322
|
+
# end
|
|
323
|
+
# end
|
|
324
|
+
def with(*expected_parameters_or_matchers, &matching_block)
|
|
325
|
+
@parameters_matcher = ParametersMatcher.new(expected_parameters_or_matchers, self, &matching_block)
|
|
178
326
|
self
|
|
179
327
|
end
|
|
180
|
-
|
|
181
|
-
|
|
328
|
+
ruby2_keywords(:with)
|
|
329
|
+
|
|
330
|
+
# Modifies expectation so that the expected method must be called with a block.
|
|
331
|
+
#
|
|
332
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
182
333
|
#
|
|
183
|
-
#
|
|
334
|
+
# @example Expected method must be called with a block.
|
|
184
335
|
# object = mock()
|
|
185
|
-
# object.expects(:expected_method).
|
|
186
|
-
#
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
# May be called multiple times on the same expectation for consecutive invocations. Also see Expectation#then.
|
|
336
|
+
# object.expects(:expected_method).with_block_given
|
|
337
|
+
# object.expected_method { 1 + 1 }
|
|
338
|
+
# # => verify succeeds
|
|
339
|
+
#
|
|
190
340
|
# object = mock()
|
|
191
|
-
# object.
|
|
192
|
-
#
|
|
193
|
-
#
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
# yielded_values_from_first_invocation # => [1]
|
|
197
|
-
# yielded_values_from_second_invocation # => [2]
|
|
198
|
-
def yields(*parameters)
|
|
199
|
-
@yield_parameters.add(*parameters)
|
|
341
|
+
# object.expects(:expected_method).with_block_given
|
|
342
|
+
# object.expected_method
|
|
343
|
+
# # => verify fails
|
|
344
|
+
def with_block_given
|
|
345
|
+
@block_matcher = BlockMatchers::BlockGiven.new
|
|
200
346
|
self
|
|
201
347
|
end
|
|
202
|
-
|
|
203
|
-
#
|
|
348
|
+
|
|
349
|
+
# Modifies expectation so that the expected method must be called without a block.
|
|
204
350
|
#
|
|
205
|
-
#
|
|
351
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
352
|
+
#
|
|
353
|
+
# @example Expected method must be called without a block.
|
|
206
354
|
# object = mock()
|
|
207
|
-
# object.expects(:expected_method).
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
#
|
|
211
|
-
#
|
|
212
|
-
# object
|
|
213
|
-
# object.
|
|
214
|
-
#
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
# object.expected_method { |*values| yielded_values_from_second_invocation << values } # second invocation
|
|
218
|
-
# yielded_values_from_first_invocation # => [[1, 2], [3]]
|
|
219
|
-
# yielded_values_from_second_invocation # => [[4], [5, 6]]
|
|
220
|
-
def multiple_yields(*parameter_groups)
|
|
221
|
-
@yield_parameters.multiple_add(*parameter_groups)
|
|
355
|
+
# object.expects(:expected_method).with_no_block_given
|
|
356
|
+
# object.expected_method
|
|
357
|
+
# # => verify succeeds
|
|
358
|
+
#
|
|
359
|
+
# object = mock()
|
|
360
|
+
# object.expects(:expected_method).with_block_given
|
|
361
|
+
# object.expected_method { 1 + 1 }
|
|
362
|
+
# # => verify fails
|
|
363
|
+
def with_no_block_given
|
|
364
|
+
@block_matcher = BlockMatchers::NoBlockGiven.new
|
|
222
365
|
self
|
|
223
366
|
end
|
|
224
|
-
|
|
225
|
-
#
|
|
226
|
-
#
|
|
367
|
+
|
|
368
|
+
# Modifies expectation so that when the expected method is called, it yields to the block with the specified +parameters+.
|
|
369
|
+
#
|
|
370
|
+
# If no +parameters+ are specified, it yields to the block without any parameters.
|
|
371
|
+
#
|
|
372
|
+
# If no block is provided, the method will still attempt to yield resulting in a +LocalJumpError+. Note that this is what would happen if a "real" (non-mock) method implementation tried to yield to a non-existent block.
|
|
373
|
+
#
|
|
374
|
+
# May be called multiple times on the same expectation for consecutive invocations.
|
|
375
|
+
#
|
|
376
|
+
# @param [*Array] parameters parameters to be yielded.
|
|
377
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
378
|
+
# @see #then
|
|
379
|
+
#
|
|
380
|
+
# @example Yield when expected method is invoked.
|
|
381
|
+
# benchmark = mock()
|
|
382
|
+
# benchmark.expects(:measure).yields
|
|
383
|
+
# yielded = false
|
|
384
|
+
# benchmark.measure { yielded = true }
|
|
385
|
+
# yielded # => true
|
|
386
|
+
#
|
|
387
|
+
# @example Yield parameters when expected method is invoked.
|
|
388
|
+
# fibonacci = mock()
|
|
389
|
+
# fibonacci.expects(:next_pair).yields(0, 1)
|
|
390
|
+
# sum = 0
|
|
391
|
+
# fibonacci.next_pair { |first, second| sum = first + second }
|
|
392
|
+
# sum # => 1
|
|
393
|
+
#
|
|
394
|
+
# @example Yield different parameters on different invocations of the expected method.
|
|
395
|
+
# fibonacci = mock()
|
|
396
|
+
# fibonacci.expects(:next_pair).yields(0, 1).then.yields(1, 1)
|
|
397
|
+
# sum = 0
|
|
398
|
+
# fibonacci.next_pair { |first, second| sum = first + second }
|
|
399
|
+
# sum # => 1
|
|
400
|
+
# fibonacci.next_pair { |first, second| sum = first + second }
|
|
401
|
+
# sum # => 2
|
|
402
|
+
def yields(*parameters)
|
|
403
|
+
multiple_yields(parameters)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# Modifies expectation so that when the expected method is called, it yields multiple times per invocation with the specified +parameter_groups+.
|
|
407
|
+
#
|
|
408
|
+
# If no block is provided, the method will still attempt to yield resulting in a +LocalJumpError+. Note that this is what would happen if a "real" (non-mock) method implementation tried to yield to a non-existent block.
|
|
409
|
+
#
|
|
410
|
+
# @param [*Array<Array>] parameter_groups each element of +parameter_groups+ should iself be an +Array+ representing the parameters to be passed to the block for a single yield. Any element of +parameter_groups+ that is not an +Array+ is wrapped in an +Array+.
|
|
411
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
412
|
+
# @see #then
|
|
413
|
+
#
|
|
414
|
+
# @example When +foreach+ is called, the stub will invoke the block twice, the first time it passes ['row1_col1', 'row1_col2'] as the parameters, and the second time it passes ['row2_col1', ''] as the parameters.
|
|
415
|
+
# csv = mock()
|
|
416
|
+
# csv.expects(:foreach).with("path/to/file.csv").multiple_yields(['row1_col1', 'row1_col2'], ['row2_col1', ''])
|
|
417
|
+
# rows = []
|
|
418
|
+
# csv.foreach { |row| rows << row }
|
|
419
|
+
# rows # => [['row1_col1', 'row1_col2'], ['row2_col1', '']]
|
|
227
420
|
#
|
|
421
|
+
# @example Yield different groups of parameters on different invocations of the expected method. Simulating a situation where the CSV file at 'path/to/file.csv' has been modified between the two calls to +foreach+.
|
|
422
|
+
# csv = mock()
|
|
423
|
+
# csv.stubs(:foreach).with("path/to/file.csv").multiple_yields(['old_row1_col1', 'old_row1_col2'], ['old_row2_col1', '']).then.multiple_yields(['new_row1_col1', ''], ['new_row2_col1', 'new_row2_col2'])
|
|
424
|
+
# rows_from_first_invocation = []
|
|
425
|
+
# rows_from_second_invocation = []
|
|
426
|
+
# csv.foreach { |row| rows_from_first_invocation << row } # first invocation
|
|
427
|
+
# csv.foreach { |row| rows_from_second_invocation << row } # second invocation
|
|
428
|
+
# rows_from_first_invocation # => [['old_row1_col1', 'old_row1_col2'], ['old_row2_col1', '']]
|
|
429
|
+
# rows_from_second_invocation # => [['new_row1_col1', ''], ['new_row2_col1', 'new_row2_col2']]
|
|
430
|
+
def multiple_yields(*parameter_groups)
|
|
431
|
+
@yield_parameters.add(*parameter_groups)
|
|
432
|
+
self
|
|
433
|
+
end
|
|
434
|
+
|
|
228
435
|
# Modifies expectation so that when the expected method is called, it returns the specified +value+.
|
|
436
|
+
#
|
|
437
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
438
|
+
# @see #then
|
|
439
|
+
#
|
|
440
|
+
# @overload def returns(value)
|
|
441
|
+
# @param [Object] value value to return on invocation of expected method.
|
|
442
|
+
# @overload def returns(*values)
|
|
443
|
+
# @param [*Array] values values to return on consecutive invocations of expected method.
|
|
444
|
+
#
|
|
445
|
+
# @example Return the same value on every invocation.
|
|
229
446
|
# object = mock()
|
|
230
447
|
# object.stubs(:stubbed_method).returns('result')
|
|
231
448
|
# object.stubbed_method # => 'result'
|
|
232
449
|
# object.stubbed_method # => 'result'
|
|
233
|
-
#
|
|
450
|
+
#
|
|
451
|
+
# @example Return a different value on consecutive invocations.
|
|
234
452
|
# object = mock()
|
|
235
453
|
# object.stubs(:stubbed_method).returns(1, 2)
|
|
236
454
|
# object.stubbed_method # => 1
|
|
237
455
|
# object.stubbed_method # => 2
|
|
238
|
-
#
|
|
456
|
+
#
|
|
457
|
+
# @example Alternative way to return a different value on consecutive invocations.
|
|
239
458
|
# object = mock()
|
|
240
459
|
# object.stubs(:expected_method).returns(1, 2).then.returns(3)
|
|
241
460
|
# object.expected_method # => 1
|
|
242
461
|
# object.expected_method # => 2
|
|
243
462
|
# object.expected_method # => 3
|
|
244
|
-
#
|
|
463
|
+
#
|
|
464
|
+
# @example May be called in conjunction with {#raises} on the same expectation.
|
|
245
465
|
# object = mock()
|
|
246
466
|
# object.stubs(:expected_method).returns(1, 2).then.raises(Exception)
|
|
247
467
|
# object.expected_method # => 1
|
|
248
468
|
# object.expected_method # => 2
|
|
249
469
|
# object.expected_method # => raises exception of class Exception1
|
|
250
|
-
# If +value+ is a +Proc+, then the expected method will return the result of calling <tt>Proc#call</tt>.
|
|
251
470
|
#
|
|
252
|
-
#
|
|
253
|
-
# Use explicit multiple return values and/or multiple expectations instead.
|
|
254
|
-
#
|
|
255
|
-
# A +Proc+ instance will be treated the same as any other value in a future release.
|
|
471
|
+
# @example Note that in Ruby a method returning multiple values is exactly equivalent to a method returning an +Array+ of those values.
|
|
256
472
|
# object = mock()
|
|
257
|
-
# object.stubs(:
|
|
258
|
-
#
|
|
259
|
-
#
|
|
473
|
+
# object.stubs(:expected_method).returns([1, 2])
|
|
474
|
+
# x, y = object.expected_method
|
|
475
|
+
# x # => 1
|
|
476
|
+
# y # => 2
|
|
260
477
|
def returns(*values)
|
|
261
478
|
@return_values += ReturnValues.build(*values)
|
|
262
479
|
self
|
|
263
480
|
end
|
|
264
|
-
|
|
265
|
-
#
|
|
481
|
+
|
|
482
|
+
# Modifies expectation so that when the expected method is called, it raises the specified +exception+ with the specified +message+ i.e. calls +Kernel#raise(exception, message)+.
|
|
266
483
|
#
|
|
267
|
-
#
|
|
268
|
-
#
|
|
269
|
-
#
|
|
484
|
+
# @param [Class,Exception,String,#exception] exception exception to be raised or message to be passed to RuntimeError.
|
|
485
|
+
# @param [String] message exception message.
|
|
486
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
487
|
+
#
|
|
488
|
+
# @see Kernel#raise
|
|
489
|
+
# @see #then
|
|
490
|
+
#
|
|
491
|
+
# @overload def raises
|
|
492
|
+
# @overload def raises(exception)
|
|
493
|
+
# @overload def raises(exception, message)
|
|
494
|
+
#
|
|
495
|
+
# @example Raise specified exception if expected method is invoked.
|
|
496
|
+
# object = stub()
|
|
497
|
+
# object.stubs(:expected_method).raises(Exception, 'message')
|
|
270
498
|
# object.expected_method # => raises exception of class Exception and with message 'message'
|
|
271
|
-
#
|
|
272
|
-
#
|
|
499
|
+
#
|
|
500
|
+
# @example Raise custom exception with extra constructor parameters by passing in an instance of the exception.
|
|
501
|
+
# object = stub()
|
|
502
|
+
# object.stubs(:expected_method).raises(MyException.new('message', 1, 2, 3))
|
|
503
|
+
# object.expected_method # => raises the specified instance of MyException
|
|
504
|
+
#
|
|
505
|
+
# @example Raise different exceptions on consecutive invocations of the expected method.
|
|
506
|
+
# object = stub()
|
|
273
507
|
# object.stubs(:expected_method).raises(Exception1).then.raises(Exception2)
|
|
274
508
|
# object.expected_method # => raises exception of class Exception1
|
|
275
509
|
# object.expected_method # => raises exception of class Exception2
|
|
276
|
-
#
|
|
277
|
-
#
|
|
510
|
+
#
|
|
511
|
+
# @example Raise an exception on first invocation of expected method and then return values on subsequent invocations.
|
|
512
|
+
# object = stub()
|
|
278
513
|
# object.stubs(:expected_method).raises(Exception).then.returns(2, 3)
|
|
279
514
|
# object.expected_method # => raises exception of class Exception1
|
|
280
515
|
# object.expected_method # => 2
|
|
@@ -284,101 +519,257 @@ module Mocha # :nodoc:
|
|
|
284
519
|
self
|
|
285
520
|
end
|
|
286
521
|
|
|
287
|
-
#
|
|
522
|
+
# Modifies expectation so that when the expected method is called, it throws the specified +tag+ with the specific return value +object+ i.e. calls +Kernel#throw(tag, object)+.
|
|
523
|
+
#
|
|
524
|
+
# @param [Symbol,String] tag tag to throw to transfer control to the active catch block.
|
|
525
|
+
# @param [Object] object return value for the catch block.
|
|
526
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
527
|
+
#
|
|
528
|
+
# @see Kernel#throw
|
|
529
|
+
# @see #then
|
|
530
|
+
#
|
|
531
|
+
# @overload def throw(tag)
|
|
532
|
+
# @overload def throw(tag, object)
|
|
533
|
+
#
|
|
534
|
+
# @example Throw tag when expected method is invoked.
|
|
535
|
+
# object = stub()
|
|
536
|
+
# object.stubs(:expected_method).throws(:done)
|
|
537
|
+
# object.expected_method # => throws tag :done
|
|
538
|
+
#
|
|
539
|
+
# @example Throw tag with return value +object+ c.f. +Kernel#throw+.
|
|
540
|
+
# object = stub()
|
|
541
|
+
# object.stubs(:expected_method).throws(:done, 'result')
|
|
542
|
+
# object.expected_method # => throws tag :done and causes catch block to return 'result'
|
|
543
|
+
#
|
|
544
|
+
# @example Throw different tags on consecutive invocations of the expected method.
|
|
545
|
+
# object = stub()
|
|
546
|
+
# object.stubs(:expected_method).throws(:done).then.throws(:continue)
|
|
547
|
+
# object.expected_method # => throws :done
|
|
548
|
+
# object.expected_method # => throws :continue
|
|
549
|
+
#
|
|
550
|
+
# @example Throw tag on first invocation of expected method and then return values for subsequent invocations.
|
|
551
|
+
# object = stub()
|
|
552
|
+
# object.stubs(:expected_method).throws(:done).then.returns(2, 3)
|
|
553
|
+
# object.expected_method # => throws :done
|
|
554
|
+
# object.expected_method # => 2
|
|
555
|
+
# object.expected_method # => 3
|
|
556
|
+
def throws(tag, object = nil)
|
|
557
|
+
@return_values += ReturnValues.new(Thrower.new(tag, object))
|
|
558
|
+
self
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
# @overload def then
|
|
562
|
+
# Used as syntactic sugar to improve readability. It has no effect on state of the expectation.
|
|
563
|
+
# @overload def then(state)
|
|
564
|
+
# Used to change the +state_machine+ to the specified state when the expected invocation occurs.
|
|
565
|
+
# @param [StateMachine::State] state state_machine.is(state_name) provides a mechanism to change the +state_machine+ into the state specified by +state_name+ when the expected method is invoked.
|
|
566
|
+
#
|
|
567
|
+
# @see API#states
|
|
568
|
+
# @see StateMachine
|
|
569
|
+
# @see #when
|
|
288
570
|
#
|
|
289
|
-
#
|
|
571
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
572
|
+
#
|
|
573
|
+
# @example Using {#then} as syntactic sugar when specifying values to be returned and exceptions to be raised on consecutive invocations of the expected method.
|
|
290
574
|
# object = mock()
|
|
291
575
|
# object.stubs(:expected_method).returns(1, 2).then.raises(Exception).then.returns(4)
|
|
292
576
|
# object.expected_method # => 1
|
|
293
577
|
# object.expected_method # => 2
|
|
294
578
|
# object.expected_method # => raises exception of class Exception
|
|
295
579
|
# object.expected_method # => 4
|
|
296
|
-
|
|
580
|
+
#
|
|
581
|
+
# @example Using {#then} to change the +state+ of a +state_machine+ on the invocation of an expected method.
|
|
582
|
+
# power = states('power').starts_as('off')
|
|
583
|
+
#
|
|
584
|
+
# radio = mock('radio')
|
|
585
|
+
# radio.expects(:switch_on).then(power.is('on'))
|
|
586
|
+
# radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
|
|
587
|
+
# radio.expects(:adjust_volume).with(+5).when(power.is('on'))
|
|
588
|
+
# radio.expects(:select_channel).with('BBC World Service').when(power.is('on'))
|
|
589
|
+
# radio.expects(:adjust_volume).with(-5).when(power.is('on'))
|
|
590
|
+
# radio.expects(:switch_off).then(power.is('off'))
|
|
591
|
+
def then(state = nil)
|
|
592
|
+
add_side_effect(ChangeStateSideEffect.new(state)) if state
|
|
297
593
|
self
|
|
298
594
|
end
|
|
299
|
-
|
|
300
|
-
#
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
595
|
+
|
|
596
|
+
# Constrains the expectation to occur only when the +state_machine+ is in the state specified by +state_predicate+.
|
|
597
|
+
#
|
|
598
|
+
# @param [StateMachine::StatePredicate] state_predicate +state_machine.is(state_name)+ provides a mechanism to determine whether the +state_machine+ is in the state specified by +state_predicate+ when the expected method is invoked.
|
|
599
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
600
|
+
#
|
|
601
|
+
# @see API#states
|
|
602
|
+
# @see StateMachine
|
|
603
|
+
# @see #then
|
|
604
|
+
#
|
|
605
|
+
# @example Using {#when} to only allow invocation of methods when "power" state machine is in the "on" state.
|
|
606
|
+
# power = states('power').starts_as('off')
|
|
607
|
+
#
|
|
608
|
+
# radio = mock('radio')
|
|
609
|
+
# radio.expects(:switch_on).then(power.is('on'))
|
|
610
|
+
# radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
|
|
611
|
+
# radio.expects(:adjust_volume).with(+5).when(power.is('on'))
|
|
612
|
+
# radio.expects(:select_channel).with('BBC World Service').when(power.is('on'))
|
|
613
|
+
# radio.expects(:adjust_volume).with(-5).when(power.is('on'))
|
|
614
|
+
# radio.expects(:switch_off).then(power.is('off'))
|
|
615
|
+
def when(state_predicate)
|
|
616
|
+
add_ordering_constraint(InStateOrderingConstraint.new(state_predicate))
|
|
617
|
+
self
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
# Constrains the expectation so that it must be invoked at the current point in the +sequence+.
|
|
621
|
+
#
|
|
622
|
+
# To expect a sequence of invocations, write the expectations in order and add the +in_sequence(sequence)+ clause to each one.
|
|
623
|
+
#
|
|
624
|
+
# Expectations in a +sequence+ can have any invocation count.
|
|
625
|
+
#
|
|
626
|
+
# If an expectation in a sequence is stubbed, rather than expected, it can be skipped in the +sequence+.
|
|
627
|
+
#
|
|
628
|
+
# An expected method can appear in multiple sequences.
|
|
629
|
+
#
|
|
630
|
+
# @param [Sequence] sequence sequence in which expected method should appear.
|
|
631
|
+
# @param [*Array<Sequence>] sequences more sequences in which expected method should appear.
|
|
632
|
+
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
|
|
633
|
+
#
|
|
634
|
+
# @see API#sequence
|
|
635
|
+
#
|
|
636
|
+
# @example Ensure methods are invoked in a specified order.
|
|
637
|
+
# breakfast = sequence('breakfast')
|
|
638
|
+
#
|
|
639
|
+
# egg = mock('egg')
|
|
640
|
+
# egg.expects(:crack).in_sequence(breakfast)
|
|
641
|
+
# egg.expects(:fry).in_sequence(breakfast)
|
|
642
|
+
# egg.expects(:eat).in_sequence(breakfast)
|
|
643
|
+
def in_sequence(sequence, *sequences)
|
|
644
|
+
sequences.unshift(sequence).each { |seq| add_in_sequence_ordering_constraint(seq) }
|
|
304
645
|
self
|
|
305
646
|
end
|
|
306
|
-
|
|
307
|
-
attr_reader :backtrace
|
|
308
647
|
|
|
309
|
-
|
|
648
|
+
# @private
|
|
649
|
+
attr_reader :backtrace_locations
|
|
650
|
+
|
|
651
|
+
# @private
|
|
652
|
+
def initialize(mock, expected_method_name, backtrace_locations = nil)
|
|
310
653
|
@mock = mock
|
|
311
|
-
@method_matcher = MethodMatcher.new(expected_method_name)
|
|
654
|
+
@method_matcher = MethodMatcher.new(expected_method_name.to_sym)
|
|
312
655
|
@parameters_matcher = ParametersMatcher.new
|
|
656
|
+
@block_matcher = BlockMatchers::OptionalBlock.new
|
|
313
657
|
@ordering_constraints = []
|
|
314
|
-
@
|
|
658
|
+
@side_effects = []
|
|
659
|
+
@cardinality = Cardinality.new.exactly(1)
|
|
315
660
|
@return_values = ReturnValues.new
|
|
316
661
|
@yield_parameters = YieldParameters.new
|
|
317
|
-
@
|
|
662
|
+
@backtrace_locations = backtrace_locations || caller_locations
|
|
318
663
|
end
|
|
319
|
-
|
|
664
|
+
|
|
665
|
+
# @private
|
|
320
666
|
def add_ordering_constraint(ordering_constraint)
|
|
321
667
|
@ordering_constraints << ordering_constraint
|
|
322
668
|
end
|
|
323
|
-
|
|
669
|
+
|
|
670
|
+
# @private
|
|
671
|
+
def add_in_sequence_ordering_constraint(sequence)
|
|
672
|
+
sequence.constrain_as_next_in_sequence(self)
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
# @private
|
|
676
|
+
def add_side_effect(side_effect)
|
|
677
|
+
@side_effects << side_effect
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
# @private
|
|
681
|
+
def perform_side_effects
|
|
682
|
+
@side_effects.each(&:perform)
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
# @private
|
|
324
686
|
def in_correct_order?
|
|
325
|
-
@ordering_constraints.all?
|
|
687
|
+
@ordering_constraints.all?(&:allows_invocation_now?)
|
|
326
688
|
end
|
|
327
|
-
|
|
689
|
+
|
|
690
|
+
# @private
|
|
691
|
+
def ordering_constraints_not_allowing_invocation_now
|
|
692
|
+
@ordering_constraints.reject(&:allows_invocation_now?)
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
# @private
|
|
328
696
|
def matches_method?(method_name)
|
|
329
697
|
@method_matcher.match?(method_name)
|
|
330
698
|
end
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
699
|
+
|
|
700
|
+
# @private
|
|
701
|
+
def match?(invocation, ignoring_order: false)
|
|
702
|
+
order_independent_match = @method_matcher.match?(invocation.method_name) && @parameters_matcher.match?(invocation.arguments) && @block_matcher.match?(invocation.block)
|
|
703
|
+
ignoring_order ? order_independent_match : order_independent_match && in_correct_order?
|
|
334
704
|
end
|
|
335
|
-
|
|
705
|
+
|
|
706
|
+
# @private
|
|
336
707
|
def invocations_allowed?
|
|
337
|
-
|
|
338
|
-
@invoked_count < @expected_count.last
|
|
339
|
-
else
|
|
340
|
-
@invoked_count < @expected_count
|
|
341
|
-
end
|
|
708
|
+
@cardinality.invocations_allowed?
|
|
342
709
|
end
|
|
343
710
|
|
|
711
|
+
# @private
|
|
712
|
+
def invocations_never_allowed?
|
|
713
|
+
@cardinality.invocations_never_allowed?
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
# @private
|
|
344
717
|
def satisfied?
|
|
345
|
-
|
|
346
|
-
@invoked_count >= @expected_count.first
|
|
347
|
-
else
|
|
348
|
-
@invoked_count >= @expected_count
|
|
349
|
-
end
|
|
718
|
+
@cardinality.satisfied?
|
|
350
719
|
end
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
720
|
+
|
|
721
|
+
# @private
|
|
722
|
+
def invoke(invocation)
|
|
723
|
+
perform_side_effects
|
|
724
|
+
@cardinality << invocation
|
|
725
|
+
invocation.call(@yield_parameters, @return_values)
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
# @private
|
|
729
|
+
def verified?(assertion_counter = nil)
|
|
730
|
+
assertion_counter.increment if assertion_counter && @cardinality.needs_verifying?
|
|
731
|
+
@cardinality.verified?
|
|
360
732
|
end
|
|
361
733
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
734
|
+
# @private
|
|
735
|
+
def used?
|
|
736
|
+
@cardinality.used?
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
# @private
|
|
740
|
+
def inspect
|
|
741
|
+
address = __id__ * 2
|
|
742
|
+
address += 0x100000000 if address < 0
|
|
743
|
+
"#<Expectation:0x#{format('%<address>x', address: address)} #{mocha_inspect} >"
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
# @private
|
|
747
|
+
def mocha_inspect
|
|
748
|
+
strings = ["#{@cardinality.anticipated_times}, #{@cardinality.invoked_times}: #{method_signature}"]
|
|
749
|
+
strings << "; #{@ordering_constraints.map(&:mocha_inspect).join('; ')}" unless @ordering_constraints.empty?
|
|
750
|
+
if Mocha.configuration.display_matching_invocations_on_failure?
|
|
751
|
+
strings << @cardinality.actual_invocations
|
|
367
752
|
end
|
|
753
|
+
strings.join
|
|
368
754
|
end
|
|
369
|
-
|
|
755
|
+
|
|
756
|
+
# @private
|
|
370
757
|
def method_signature
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
758
|
+
strings = ["#{@mock.mocha_inspect}.#{@method_matcher.mocha_inspect}#{@parameters_matcher.mocha_inspect}"]
|
|
759
|
+
strings << " #{@block_matcher.mocha_inspect}" if @block_matcher.mocha_inspect
|
|
760
|
+
strings.join
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
# @private
|
|
764
|
+
def backtrace
|
|
765
|
+
backtrace_locations.map(&:to_s)
|
|
374
766
|
end
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
767
|
+
|
|
768
|
+
# @private
|
|
769
|
+
def definition_location
|
|
770
|
+
filter = BacktraceFilter.new
|
|
771
|
+
location = filter.filtered_locations(backtrace_locations)[0]
|
|
772
|
+
"#{location.path}:#{location.lineno}"
|
|
378
773
|
end
|
|
379
|
-
|
|
380
|
-
# :startdoc:
|
|
381
|
-
|
|
382
774
|
end
|
|
383
|
-
|
|
384
|
-
end
|
|
775
|
+
end
|