mocha 0.5.6 → 3.0.2
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 +1235 -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 +15 -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 +19 -14
- 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 +553 -168
- 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 +56 -22
- 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 +13 -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 +199 -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/mise.toml +2 -0
- data/mocha.gemspec +40 -0
- metadata +130 -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
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'mocha/central'
|
|
4
|
+
require 'mocha/mock'
|
|
5
|
+
require 'mocha/name'
|
|
6
|
+
require 'mocha/impersonating_name'
|
|
7
|
+
require 'mocha/impersonating_any_instance_name'
|
|
8
|
+
require 'mocha/object_receiver'
|
|
9
|
+
require 'mocha/any_instance_receiver'
|
|
10
|
+
require 'mocha/state_machine'
|
|
11
|
+
require 'mocha/logger'
|
|
12
|
+
require 'mocha/configuration'
|
|
13
|
+
require 'mocha/stubbing_error'
|
|
14
|
+
require 'mocha/not_initialized_error'
|
|
15
|
+
require 'mocha/expectation_error_factory'
|
|
16
|
+
|
|
17
|
+
module Mocha
|
|
18
|
+
class Mockery
|
|
19
|
+
class Null < self
|
|
20
|
+
def self.build
|
|
21
|
+
new(nil)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def add_mock(*)
|
|
25
|
+
raise_not_initialized_error
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def add_state_machine(*)
|
|
29
|
+
raise_not_initialized_error
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def stubba
|
|
33
|
+
Central::Null.new(&method(:raise_not_initialized_error))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def raise_not_initialized_error
|
|
39
|
+
message = 'Mocha methods cannot be used outside the context of a test'
|
|
40
|
+
raise NotInitializedError.new(message, caller)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class << self
|
|
45
|
+
def instance
|
|
46
|
+
@instances.last || Null.build
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def setup(assertion_counter)
|
|
50
|
+
@instances ||= []
|
|
51
|
+
mockery = new(assertion_counter)
|
|
52
|
+
mockery.logger = instance.logger unless @instances.empty?
|
|
53
|
+
@instances.push(mockery)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def verify
|
|
57
|
+
instance.verify
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def teardown(origin = nil)
|
|
61
|
+
if @instances.nil?
|
|
62
|
+
raise NotInitializedError, 'Mocha::Mockery.teardown called before Mocha::Mockery.setup'
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
instance.teardown(origin)
|
|
66
|
+
ensure
|
|
67
|
+
@instances.pop unless @instances.nil?
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def initialize(assertion_counter)
|
|
72
|
+
@assertion_counter = assertion_counter
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def named_mock(name)
|
|
76
|
+
add_mock(Mock.new(self, @assertion_counter, Name.new(name)))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def unnamed_mock
|
|
80
|
+
add_mock(Mock.new(self, @assertion_counter))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def mock_impersonating(object)
|
|
84
|
+
add_mock(Mock.new(self, @assertion_counter, ImpersonatingName.new(object), ObjectReceiver.new(object)))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def mock_impersonating_any_instance_of(klass)
|
|
88
|
+
add_mock(Mock.new(self, @assertion_counter, ImpersonatingAnyInstanceName.new(klass), AnyInstanceReceiver.new(klass)))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def new_state_machine(name)
|
|
92
|
+
add_state_machine(StateMachine.new(name))
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def verify
|
|
96
|
+
unless mocks.all?(&:__verified__?)
|
|
97
|
+
message = "not all expectations were satisfied\n#{mocha_inspect}"
|
|
98
|
+
backtrace = if unsatisfied_expectations.empty?
|
|
99
|
+
caller
|
|
100
|
+
else
|
|
101
|
+
unsatisfied_expectations[0].backtrace
|
|
102
|
+
end
|
|
103
|
+
raise ExpectationErrorFactory.build(message, backtrace)
|
|
104
|
+
end
|
|
105
|
+
expectations.reject(&:used?).each do |expectation|
|
|
106
|
+
signature_proc = lambda { expectation.method_signature }
|
|
107
|
+
check(:stubbing_method_unnecessarily, 'method unnecessarily', signature_proc, expectation.backtrace)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def teardown(origin = nil)
|
|
112
|
+
stubba.unstub_all
|
|
113
|
+
mocks.each { |m| m.__expire__(origin) }
|
|
114
|
+
reset
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def stubba
|
|
118
|
+
@stubba ||= Central.new
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def mocks
|
|
122
|
+
@mocks ||= []
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def state_machines
|
|
126
|
+
@state_machines ||= []
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def sequences
|
|
130
|
+
@sequences ||= []
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def mocha_inspect
|
|
134
|
+
lines = []
|
|
135
|
+
lines << "unsatisfied expectations:\n- #{unsatisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if unsatisfied_expectations.any?
|
|
136
|
+
lines << "satisfied expectations:\n- #{satisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if satisfied_expectations.any?
|
|
137
|
+
lines << "states:\n- #{state_machines.map(&:mocha_inspect).join("\n- ")}\n" if state_machines.any?
|
|
138
|
+
lines.join
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def on_stubbing(object, method)
|
|
142
|
+
signature_proc = lambda { "#{object.mocha_inspect}.#{method}" }
|
|
143
|
+
check(:stubbing_non_existent_method, 'non-existent method', signature_proc) do
|
|
144
|
+
!(object.stubba_class.__method_exists__?(method) || object.stubba_respond_to?(method))
|
|
145
|
+
end
|
|
146
|
+
check(:stubbing_non_public_method, 'non-public method', signature_proc) do
|
|
147
|
+
object.stubba_class.__method_exists__?(method, include_public_methods: false)
|
|
148
|
+
end
|
|
149
|
+
check(:stubbing_method_on_non_mock_object, 'method on non-mock object', signature_proc)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
attr_writer :logger
|
|
153
|
+
|
|
154
|
+
def logger
|
|
155
|
+
@logger ||= Logger.new($stderr)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
private
|
|
159
|
+
|
|
160
|
+
def check(action, description, signature_proc, backtrace = caller)
|
|
161
|
+
treatment = Mocha.configuration.send(action)
|
|
162
|
+
return if (treatment == :allow) || (block_given? && !yield)
|
|
163
|
+
|
|
164
|
+
method_signature = signature_proc.call
|
|
165
|
+
message = "stubbing #{description}: #{method_signature}"
|
|
166
|
+
raise StubbingError.new(message, backtrace) if treatment == :prevent
|
|
167
|
+
|
|
168
|
+
logger.warn(message) if treatment == :warn
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def expectations
|
|
172
|
+
mocks.map { |mock| mock.__expectations__.to_a }.flatten
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def unsatisfied_expectations
|
|
176
|
+
expectations.reject(&:verified?)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def satisfied_expectations
|
|
180
|
+
expectations.select(&:verified?)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def add_mock(mock)
|
|
184
|
+
mocks << mock
|
|
185
|
+
mock
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def add_state_machine(state_machine)
|
|
189
|
+
state_machines << state_machine
|
|
190
|
+
state_machine
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def reset
|
|
194
|
+
@stubba = nil
|
|
195
|
+
@mocks = nil
|
|
196
|
+
@state_machines = nil
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
data/lib/mocha/name.rb
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'mocha/mockery'
|
|
4
|
+
require 'mocha/instance_method'
|
|
5
|
+
require 'mocha/argument_iterator'
|
|
6
|
+
require 'mocha/expectation_error_factory'
|
|
7
|
+
require 'mocha/ignoring_warning'
|
|
8
|
+
|
|
9
|
+
module Mocha
|
|
10
|
+
# Methods added to all objects to allow mocking and stubbing on real (i.e. non-mock) objects.
|
|
11
|
+
#
|
|
12
|
+
# Both {#expects} and {#stubs} return an {Expectation} which can be further modified by methods on {Expectation}.
|
|
13
|
+
module ObjectMethods
|
|
14
|
+
extend IgnoringWarning
|
|
15
|
+
|
|
16
|
+
# @private
|
|
17
|
+
JRUBY_ALIAS_SPECIAL_METHODS_WARNING = /accesses caller method's state and should not be aliased/.freeze
|
|
18
|
+
|
|
19
|
+
ignoring_warning(JRUBY_ALIAS_SPECIAL_METHODS_WARNING, if_: RUBY_ENGINE == 'jruby') do
|
|
20
|
+
alias_method :_method, :method
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @private
|
|
24
|
+
def mocha(instantiate: true)
|
|
25
|
+
if instantiate
|
|
26
|
+
@mocha ||= Mocha::Mockery.instance.mock_impersonating(self)
|
|
27
|
+
else
|
|
28
|
+
defined?(@mocha) ? @mocha : nil
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @private
|
|
33
|
+
def reset_mocha
|
|
34
|
+
@mocha = nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @private
|
|
38
|
+
def stubba_method
|
|
39
|
+
Mocha::InstanceMethod
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @private
|
|
43
|
+
def stubba_object
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @private
|
|
48
|
+
def stubba_class
|
|
49
|
+
singleton_class
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @private
|
|
53
|
+
def stubba_respond_to?(symbol)
|
|
54
|
+
respond_to?(symbol)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Adds an expectation that the specified method must be called exactly once with any parameters.
|
|
58
|
+
#
|
|
59
|
+
# The original implementation of the method is replaced during the test and then restored at the end of the test. The temporary replacement method has the same visibility as the original method.
|
|
60
|
+
#
|
|
61
|
+
# @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
|
|
62
|
+
# @raise [StubbingError] if attempting to stub method which is not allowed.
|
|
63
|
+
#
|
|
64
|
+
# @overload def expects(method_name)
|
|
65
|
+
# @param [Symbol,String] method_name name of expected method
|
|
66
|
+
# @overload def expects(expected_methods_vs_return_values)
|
|
67
|
+
# @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {#expects} were called multiple times.
|
|
68
|
+
#
|
|
69
|
+
# @example Setting up an expectation on a non-mock object.
|
|
70
|
+
# product = Product.new
|
|
71
|
+
# product.expects(:save).returns(true)
|
|
72
|
+
# assert_equal true, product.save
|
|
73
|
+
#
|
|
74
|
+
# @example Setting up multiple expectations on a non-mock object.
|
|
75
|
+
# product = Product.new
|
|
76
|
+
# product.expects(valid?: true, save: true)
|
|
77
|
+
#
|
|
78
|
+
# # exactly equivalent to
|
|
79
|
+
#
|
|
80
|
+
# product = Product.new
|
|
81
|
+
# product.expects(:valid?).returns(true)
|
|
82
|
+
# product.expects(:save).returns(true)
|
|
83
|
+
#
|
|
84
|
+
# @see Mock#expects
|
|
85
|
+
def expects(expected_methods_vs_return_values)
|
|
86
|
+
if expected_methods_vs_return_values.to_s =~ /the[^a-z]*spanish[^a-z]*inquisition/i
|
|
87
|
+
raise ExpectationErrorFactory.build('NOBODY EXPECTS THE SPANISH INQUISITION!')
|
|
88
|
+
end
|
|
89
|
+
if frozen?
|
|
90
|
+
raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}", caller)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
expectation = nil
|
|
94
|
+
mockery = Mocha::Mockery.instance
|
|
95
|
+
iterator = ArgumentIterator.new(expected_methods_vs_return_values)
|
|
96
|
+
iterator.each do |method_name, *args|
|
|
97
|
+
mockery.on_stubbing(self, method_name)
|
|
98
|
+
method = stubba_method.new(stubba_object, method_name)
|
|
99
|
+
mockery.stubba.stub(method)
|
|
100
|
+
expectation = mocha.expects(method_name, caller)
|
|
101
|
+
expectation.returns(args.shift) unless args.empty?
|
|
102
|
+
end
|
|
103
|
+
expectation
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Adds an expectation that the specified method may be called any number of times with any parameters.
|
|
107
|
+
#
|
|
108
|
+
# The original implementation of the method is replaced during the test and then restored at the end of the test. The temporary replacement method has the same visibility as the original method.
|
|
109
|
+
#
|
|
110
|
+
# @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
|
|
111
|
+
# @raise [StubbingError] if attempting to stub method which is not allowed.
|
|
112
|
+
#
|
|
113
|
+
# @overload def stubs(method_name)
|
|
114
|
+
# @param [Symbol,String] method_name name of stubbed method
|
|
115
|
+
# @overload def stubs(stubbed_methods_vs_return_values)
|
|
116
|
+
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {#stubs} were called multiple times.
|
|
117
|
+
#
|
|
118
|
+
# @example Setting up a stubbed methods on a non-mock object.
|
|
119
|
+
# product = Product.new
|
|
120
|
+
# product.stubs(:save).returns(true)
|
|
121
|
+
# assert_equal true, product.save
|
|
122
|
+
#
|
|
123
|
+
# @example Setting up multiple stubbed methods on a non-mock object.
|
|
124
|
+
# product = Product.new
|
|
125
|
+
# product.stubs(valid?: true, save: true)
|
|
126
|
+
#
|
|
127
|
+
# # exactly equivalent to
|
|
128
|
+
#
|
|
129
|
+
# product = Product.new
|
|
130
|
+
# product.stubs(:valid?).returns(true)
|
|
131
|
+
# product.stubs(:save).returns(true)
|
|
132
|
+
#
|
|
133
|
+
# @see Mock#stubs
|
|
134
|
+
def stubs(stubbed_methods_vs_return_values)
|
|
135
|
+
if frozen?
|
|
136
|
+
raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}", caller)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
expectation = nil
|
|
140
|
+
mockery = Mocha::Mockery.instance
|
|
141
|
+
iterator = ArgumentIterator.new(stubbed_methods_vs_return_values)
|
|
142
|
+
iterator.each do |method_name, *args|
|
|
143
|
+
mockery.on_stubbing(self, method_name)
|
|
144
|
+
method = stubba_method.new(stubba_object, method_name)
|
|
145
|
+
mockery.stubba.stub(method)
|
|
146
|
+
expectation = mocha.stubs(method_name, caller)
|
|
147
|
+
expectation.returns(args.shift) unless args.empty?
|
|
148
|
+
end
|
|
149
|
+
expectation
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Removes the specified stubbed methods (added by calls to {#expects} or {#stubs}) and all expectations associated with them.
|
|
153
|
+
#
|
|
154
|
+
# Restores the original behaviour of the methods before they were stubbed. This is normally done automatically at the end of each test, but in some circumstances you may want to do it *before* the end of the test.
|
|
155
|
+
#
|
|
156
|
+
# WARNING: If you {#unstub} a method which still has unsatisfied expectations, you may be removing the only way those expectations can be satisfied. Use {#unstub} with care.
|
|
157
|
+
#
|
|
158
|
+
# @param [Array<Symbol>] method_names names of methods to unstub.
|
|
159
|
+
#
|
|
160
|
+
# @example Stubbing and unstubbing a method on a real (non-mock) object.
|
|
161
|
+
# multiplier = Multiplier.new
|
|
162
|
+
# multiplier.double(2) # => 4
|
|
163
|
+
# multiplier.stubs(:double).raises # new behaviour defined
|
|
164
|
+
# multiplier.double(2) # => raises exception
|
|
165
|
+
# multiplier.unstub(:double) # original behaviour restored
|
|
166
|
+
# multiplier.double(2) # => 4
|
|
167
|
+
#
|
|
168
|
+
# @example Unstubbing multiple methods on a real (non-mock) object.
|
|
169
|
+
# multiplier.unstub(:double, :triple)
|
|
170
|
+
#
|
|
171
|
+
# # exactly equivalent to
|
|
172
|
+
#
|
|
173
|
+
# multiplier.unstub(:double)
|
|
174
|
+
# multiplier.unstub(:triple)
|
|
175
|
+
def unstub(*method_names)
|
|
176
|
+
mockery = Mocha::Mockery.instance
|
|
177
|
+
method_names.each do |method_name|
|
|
178
|
+
method = stubba_method.new(stubba_object, method_name)
|
|
179
|
+
mockery.stubba.unstub(method)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mocha
|
|
4
|
+
class ObjectReceiver
|
|
5
|
+
def initialize(object)
|
|
6
|
+
@object = object
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def mocks
|
|
10
|
+
object = @object
|
|
11
|
+
mocks = []
|
|
12
|
+
while object
|
|
13
|
+
mocha = object.mocha(instantiate: false)
|
|
14
|
+
mocks << mocha if mocha
|
|
15
|
+
object = object.is_a?(Class) ? object.superclass : nil
|
|
16
|
+
end
|
|
17
|
+
mocks
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -1,42 +1,52 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'mocha/parameter_matchers/base_methods'
|
|
2
4
|
|
|
3
5
|
module Mocha
|
|
4
|
-
|
|
5
6
|
module ParameterMatchers
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
7
|
+
module Methods
|
|
8
|
+
# Matches if all +matchers+ match.
|
|
9
|
+
#
|
|
10
|
+
# @param [*Array<BaseMethods>] matchers parameter matchers.
|
|
11
|
+
# @return [AllOf] parameter matcher.
|
|
12
|
+
#
|
|
13
|
+
# @see Expectation#with
|
|
14
|
+
#
|
|
15
|
+
# @example All parameter matchers match.
|
|
16
|
+
# object = mock()
|
|
17
|
+
# object.expects(:method_1).with(all_of(includes(1), includes(3)))
|
|
18
|
+
# object.method_1([1, 3])
|
|
19
|
+
# # no error raised
|
|
20
|
+
#
|
|
21
|
+
# @example One of the parameter matchers does not match.
|
|
22
|
+
# object = mock()
|
|
23
|
+
# object.expects(:method_1).with(all_of(includes(1), includes(3)))
|
|
24
|
+
# object.method_1([1, 2])
|
|
25
|
+
# # error raised, because method_1 was not called with object including 1 and 3
|
|
26
|
+
def all_of(*matchers)
|
|
27
|
+
AllOf.new(*matchers)
|
|
28
|
+
end
|
|
21
29
|
end
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
|
|
31
|
+
# Parameter matcher which combines a number of other matchers using a logical AND.
|
|
32
|
+
class AllOf
|
|
33
|
+
include BaseMethods
|
|
34
|
+
|
|
35
|
+
# @private
|
|
25
36
|
def initialize(*matchers)
|
|
26
37
|
@matchers = matchers
|
|
27
38
|
end
|
|
28
|
-
|
|
39
|
+
|
|
40
|
+
# @private
|
|
29
41
|
def matches?(available_parameters)
|
|
30
42
|
parameter = available_parameters.shift
|
|
31
|
-
@matchers.all? { |matcher| matcher.matches?([parameter]) }
|
|
43
|
+
@matchers.all? { |matcher| matcher.to_matcher.matches?([parameter]) }
|
|
32
44
|
end
|
|
33
|
-
|
|
45
|
+
|
|
46
|
+
# @private
|
|
34
47
|
def mocha_inspect
|
|
35
|
-
"all_of(#{@matchers.map
|
|
48
|
+
"all_of(#{@matchers.map(&:mocha_inspect).join(', ')})"
|
|
36
49
|
end
|
|
37
|
-
|
|
38
50
|
end
|
|
39
|
-
|
|
40
51
|
end
|
|
41
|
-
|
|
42
|
-
end
|
|
52
|
+
end
|
|
@@ -1,47 +1,58 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'mocha/parameter_matchers/base_methods'
|
|
2
4
|
|
|
3
5
|
module Mocha
|
|
4
|
-
|
|
5
6
|
module ParameterMatchers
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
7
|
+
module Methods
|
|
8
|
+
# Matches if any +matchers+ match.
|
|
9
|
+
#
|
|
10
|
+
# @param [*Array<BaseMethods>] matchers parameter matchers.
|
|
11
|
+
# @return [AnyOf] parameter matcher.
|
|
12
|
+
#
|
|
13
|
+
# @see Expectation#with
|
|
14
|
+
#
|
|
15
|
+
# @example One parameter matcher matches.
|
|
16
|
+
# object = mock()
|
|
17
|
+
# object.expects(:method_1).with(any_of(1, 3))
|
|
18
|
+
# object.method_1(1)
|
|
19
|
+
# # no error raised
|
|
20
|
+
#
|
|
21
|
+
# @example The other parameter matcher matches.
|
|
22
|
+
# object = mock()
|
|
23
|
+
# object.expects(:method_1).with(any_of(1, 3))
|
|
24
|
+
# object.method_1(3)
|
|
25
|
+
# # no error raised
|
|
26
|
+
#
|
|
27
|
+
# @example Neither parameter matcher matches.
|
|
28
|
+
# object = mock()
|
|
29
|
+
# object.expects(:method_1).with(any_of(1, 3))
|
|
30
|
+
# object.method_1(2)
|
|
31
|
+
# # error raised, because method_1 was not called with 1 or 3
|
|
32
|
+
def any_of(*matchers)
|
|
33
|
+
AnyOf.new(*matchers)
|
|
34
|
+
end
|
|
26
35
|
end
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
|
|
37
|
+
# Parameter matcher which combines a number of other matchers using a logical OR.
|
|
38
|
+
class AnyOf
|
|
39
|
+
include BaseMethods
|
|
40
|
+
|
|
41
|
+
# @private
|
|
30
42
|
def initialize(*matchers)
|
|
31
43
|
@matchers = matchers
|
|
32
44
|
end
|
|
33
|
-
|
|
45
|
+
|
|
46
|
+
# @private
|
|
34
47
|
def matches?(available_parameters)
|
|
35
48
|
parameter = available_parameters.shift
|
|
36
|
-
@matchers.any? { |matcher| matcher.matches?([parameter]) }
|
|
49
|
+
@matchers.any? { |matcher| matcher.to_matcher.matches?([parameter]) }
|
|
37
50
|
end
|
|
38
|
-
|
|
51
|
+
|
|
52
|
+
# @private
|
|
39
53
|
def mocha_inspect
|
|
40
|
-
"any_of(#{@matchers.map
|
|
54
|
+
"any_of(#{@matchers.map(&:mocha_inspect).join(', ')})"
|
|
41
55
|
end
|
|
42
|
-
|
|
43
56
|
end
|
|
44
|
-
|
|
45
57
|
end
|
|
46
|
-
|
|
47
|
-
end
|
|
58
|
+
end
|
|
@@ -1,40 +1,47 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'mocha/parameter_matchers/base_methods'
|
|
2
4
|
|
|
3
5
|
module Mocha
|
|
4
|
-
|
|
5
6
|
module ParameterMatchers
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
7
|
+
module Methods
|
|
8
|
+
# Matches any parameters. This is used as the default for a newly built expectation.
|
|
9
|
+
#
|
|
10
|
+
# @return [AnyParameters] parameter matcher.
|
|
11
|
+
#
|
|
12
|
+
# @see Expectation#with
|
|
13
|
+
#
|
|
14
|
+
# @example Any parameters will match.
|
|
15
|
+
# object = mock()
|
|
16
|
+
# object.expects(:method_1).with(any_parameters)
|
|
17
|
+
# object.method_1(1, 2, 3, 4)
|
|
18
|
+
# # no error raised
|
|
19
|
+
#
|
|
20
|
+
# object = mock()
|
|
21
|
+
# object.expects(:method_1).with(any_parameters)
|
|
22
|
+
# object.method_1(5, 6, 7, 8, 9, 0)
|
|
23
|
+
# # no error raised
|
|
24
|
+
def any_parameters
|
|
25
|
+
AnyParameters.new
|
|
26
|
+
end
|
|
21
27
|
end
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
# Parameter matcher which always matches whatever the parameters.
|
|
30
|
+
class AnyParameters
|
|
31
|
+
include BaseMethods
|
|
32
|
+
|
|
33
|
+
# @private
|
|
25
34
|
def matches?(available_parameters)
|
|
26
|
-
|
|
35
|
+
until available_parameters.empty?
|
|
27
36
|
available_parameters.shift
|
|
28
37
|
end
|
|
29
|
-
|
|
38
|
+
true
|
|
30
39
|
end
|
|
31
40
|
|
|
41
|
+
# @private
|
|
32
42
|
def mocha_inspect
|
|
33
|
-
|
|
43
|
+
'any_parameters'
|
|
34
44
|
end
|
|
35
|
-
|
|
36
45
|
end
|
|
37
|
-
|
|
38
46
|
end
|
|
39
|
-
|
|
40
|
-
end
|
|
47
|
+
end
|