flexmock 1.3.2 → 1.3.3
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/Gemfile.lock +9 -9
- data/Rakefile +3 -1
- data/lib/flexmock.rb +1 -1
- data/lib/flexmock/argument_matchers.rb +1 -1
- data/lib/flexmock/argument_types.rb +1 -2
- data/lib/flexmock/base.rb +2 -1
- data/lib/flexmock/call_record.rb +30 -0
- data/lib/flexmock/call_validator.rb +68 -0
- data/lib/flexmock/composite_expectation.rb +56 -0
- data/lib/flexmock/core.rb +30 -64
- data/lib/flexmock/core_class_methods.rb +1 -1
- data/lib/flexmock/default_framework_adapter.rb +1 -1
- data/lib/flexmock/deprecated_methods.rb +8 -3
- data/lib/flexmock/errors.rb +1 -1
- data/lib/flexmock/expectation.rb +7 -84
- data/lib/flexmock/expectation_builder.rb +133 -0
- data/lib/flexmock/expectation_director.rb +6 -4
- data/lib/flexmock/expectation_recorder.rb +42 -0
- data/lib/flexmock/extensions/active_record_model.rb +109 -0
- data/lib/flexmock/mock_builder.rb +139 -0
- data/lib/flexmock/mock_container.rb +13 -214
- data/lib/flexmock/noop.rb +1 -1
- data/lib/flexmock/ordering.rb +1 -2
- data/lib/flexmock/partial_mock.rb +95 -57
- data/lib/flexmock/rails.rb +1 -1
- data/lib/flexmock/recorder.rb +4 -3
- data/lib/flexmock/rspec.rb +1 -1
- data/lib/flexmock/rspec_spy_matcher.rb +4 -0
- data/lib/flexmock/spy_describers.rb +22 -5
- data/lib/flexmock/test_unit.rb +2 -40
- data/lib/flexmock/test_unit_integration.rb +4 -4
- data/lib/flexmock/test_unit_testcase_extensions.rb +49 -0
- data/lib/flexmock/undefined.rb +1 -1
- data/lib/flexmock/validators.rb +18 -12
- data/lib/flexmock/version.rb +1 -1
- data/test/based_partials_test.rb +1 -1
- data/test/container_methods_test.rb +1 -1
- data/test/default_framework_adapter_test.rb +1 -1
- data/test/demeter_mocking_test.rb +52 -2
- data/test/deprecated_methods_test.rb +1 -1
- data/test/examples_from_readme_test.rb +1 -1
- data/test/expectation_description_test.rb +1 -1
- data/test/extended_should_receive_test.rb +1 -1
- data/test/mock_builder_test.rb +68 -0
- data/test/naming_test.rb +1 -1
- data/test/new_instances_test.rb +1 -1
- data/test/partial_mock_test.rb +23 -6
- data/test/record_mode_test.rb +1 -1
- data/test/rspec_integration/integration_spec.rb +1 -1
- data/test/samples_test.rb +1 -2
- data/test/should_ignore_missing_test.rb +1 -1
- data/test/should_receive_test.rb +1 -1
- data/test/test_setup.rb +17 -0
- data/test/test_unit_integration/auto_test_unit_test.rb +1 -1
- data/test/tu_integration_test.rb +1 -1
- data/test/undefined_test.rb +1 -1
- metadata +52 -52
- data/TAGS +0 -1056
- data/lib/flexmock/composite.rb +0 -10
- data/lib/flexmock/rails/view_mocking.rb +0 -144
- data/test/rails_view_stub_test.rb +0 -145
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
#---
|
4
|
-
# Copyright 2003-
|
4
|
+
# Copyright 2003-2013 by Jim Weirich (jim.weirich@gmail.com).
|
5
5
|
# All rights reserved.
|
6
6
|
|
7
7
|
# Permission is granted for use, copying, modification, distribution,
|
@@ -12,10 +12,11 @@
|
|
12
12
|
require 'flexmock/noop'
|
13
13
|
require 'flexmock/argument_types'
|
14
14
|
require 'flexmock/ordering'
|
15
|
+
require 'flexmock/mock_builder'
|
15
16
|
|
16
17
|
class FlexMock
|
17
18
|
|
18
|
-
|
19
|
+
|
19
20
|
# Mock container methods
|
20
21
|
#
|
21
22
|
# Include this module in to get integration with FlexMock. When this module
|
@@ -116,60 +117,10 @@ class FlexMock
|
|
116
117
|
# for a partial mock, flexmock will return the domain object rather than
|
117
118
|
# the mock object.
|
118
119
|
#
|
119
|
-
def flexmock(*args)
|
120
|
+
def flexmock(*args, &block)
|
120
121
|
location = caller.first
|
121
|
-
|
122
|
-
|
123
|
-
domain_obj = nil
|
124
|
-
safe_mode = false
|
125
|
-
model_class = nil
|
126
|
-
base_class = nil
|
127
|
-
mock = nil
|
128
|
-
while ! args.empty?
|
129
|
-
case args.first
|
130
|
-
when :base, :safe
|
131
|
-
safe_mode = (args.shift == :safe)
|
132
|
-
domain_obj = args.shift
|
133
|
-
when :model
|
134
|
-
args.shift
|
135
|
-
model_class = args.shift
|
136
|
-
when :on
|
137
|
-
args.shift
|
138
|
-
base_class = args.shift
|
139
|
-
name ||= "#{base_class} Mock"
|
140
|
-
when String, Symbol
|
141
|
-
name = args.shift.to_s
|
142
|
-
when Hash
|
143
|
-
quick_defs = args.shift
|
144
|
-
when FlexMock
|
145
|
-
mock = args.shift
|
146
|
-
else
|
147
|
-
domain_obj = args.shift
|
148
|
-
end
|
149
|
-
end
|
150
|
-
raise UsageError, "a block is required in safe mode" if safe_mode && ! block_given?
|
151
|
-
|
152
|
-
if domain_obj
|
153
|
-
mock = ContainerHelper.make_partial_proxy(self, domain_obj, name, safe_mode)
|
154
|
-
result = domain_obj
|
155
|
-
elsif model_class
|
156
|
-
id = ContainerHelper.next_id
|
157
|
-
mock ||= FlexMock.new("#{model_class}_#{id}", self)
|
158
|
-
result = mock
|
159
|
-
else
|
160
|
-
mock ||= FlexMock.new(name || "unknown", self)
|
161
|
-
result = mock
|
162
|
-
end
|
163
|
-
if base_class
|
164
|
-
mock.flexmock_based_on(base_class)
|
165
|
-
elsif domain_obj && FlexMock.partials_are_based
|
166
|
-
mock.flexmock_based_on(domain_obj.class)
|
167
|
-
end
|
168
|
-
mock.flexmock_define_expectation(location, quick_defs)
|
169
|
-
yield(mock) if block_given?
|
170
|
-
flexmock_remember(mock)
|
171
|
-
ContainerHelper.add_model_methods(mock, model_class, id, location) if model_class
|
172
|
-
result
|
122
|
+
@flexmock_worker ||= MockBuilder.new(self)
|
123
|
+
@flexmock_worker.define_a_mock(location, *args, &block)
|
173
124
|
end
|
174
125
|
alias flexstub flexmock
|
175
126
|
|
@@ -187,171 +138,19 @@ class FlexMock
|
|
187
138
|
# indicate the test isn't over yet. From our point of view we are
|
188
139
|
# only interested if the test has actually failed, so we wrap the
|
189
140
|
# raw call to passed? and handle accordingly.
|
190
|
-
def flexmock_test_has_failed?
|
141
|
+
def flexmock_test_has_failed? # :nodoc:
|
191
142
|
passed? == false
|
192
143
|
end
|
193
144
|
end
|
194
145
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
# testing framework test cases. Since we don't want to pollute the
|
199
|
-
# method namespace of the class that mixes in MockContainer, a
|
200
|
-
# number of MockContainer methods were moved into ContainerHelper to
|
201
|
-
# to isoloate the names.
|
202
|
-
#
|
203
|
-
class MockContainerHelper
|
204
|
-
include FlexMock::ArgumentTypes
|
205
|
-
|
206
|
-
# Return the next id for mocked models.
|
207
|
-
def next_id
|
208
|
-
@id_counter ||= 10000
|
209
|
-
@id_counter += 1
|
210
|
-
end
|
211
|
-
|
212
|
-
# :call-seq:
|
213
|
-
# parse_should_args(args) { |symbol| ... }
|
214
|
-
#
|
215
|
-
# This method provides common handling for the various should_receive
|
216
|
-
# argument lists. It sorts out the differences between symbols, arrays and
|
217
|
-
# hashes, and identifies the method names specified by each. As each
|
218
|
-
# method name is identified, create a mock expectation for it using the
|
219
|
-
# supplied block.
|
220
|
-
def parse_should_args(mock, args, &block) # :nodoc:
|
221
|
-
result = CompositeExpectation.new
|
222
|
-
args.each do |arg|
|
223
|
-
case arg
|
224
|
-
when Hash
|
225
|
-
arg.each do |k,v|
|
226
|
-
exp = build_demeter_chain(mock, k, &block).and_return(v)
|
227
|
-
result.add(exp)
|
228
|
-
end
|
229
|
-
when Symbol, String
|
230
|
-
result.add(build_demeter_chain(mock, arg, &block))
|
231
|
-
end
|
232
|
-
end
|
233
|
-
result
|
234
|
-
end
|
235
|
-
|
236
|
-
# Automatically add mocks for some common methods in ActiveRecord
|
237
|
-
# models.
|
238
|
-
def add_model_methods(mock, model_class, id, location)
|
239
|
-
container = mock.flexmock_container
|
240
|
-
|
241
|
-
mock_errors = container.flexmock("errors")
|
242
|
-
mock_errors.flexmock_define_expectation(location, :count).and_return(0).by_default
|
243
|
-
mock_errors.flexmock_define_expectation(location, :full_messages).and_return([]).by_default
|
244
|
-
|
245
|
-
mock.flexmock_define_expectation(location, :id).and_return(id).by_default
|
246
|
-
mock.flexmock_define_expectation(location, :to_params).and_return(id.to_s).by_default
|
247
|
-
mock.flexmock_define_expectation(location, :new_record?).and_return(false).by_default
|
248
|
-
mock.flexmock_define_expectation(location, :class).and_return(model_class).by_default
|
249
|
-
mock.flexmock_define_expectation(location, :errors).and_return(mock_errors).by_default
|
250
|
-
|
251
|
-
# HACK: Ruby 1.9 needs the following lambda so that model_class
|
252
|
-
# is correctly bound below.
|
253
|
-
lambda { }
|
254
|
-
mock.flexmock_define_expectation(location, :is_a?).with(any).and_return { |other|
|
255
|
-
other == model_class
|
256
|
-
}.by_default
|
257
|
-
mock.flexmock_define_expectation(location, :instance_of?).with(any).and_return { |other|
|
258
|
-
other == model_class
|
259
|
-
}.by_default
|
260
|
-
mock.flexmock_define_expectation(location, :kind_of?).with(any).and_return { |other|
|
261
|
-
model_class.ancestors.include?(other)
|
262
|
-
}.by_default
|
263
|
-
end
|
264
|
-
|
265
|
-
# Create a PartialMockProxy for the given object. Use +name+ as
|
266
|
-
# the name of the mock object.
|
267
|
-
def make_partial_proxy(container, obj, name, safe_mode)
|
268
|
-
name ||= "flexmock(#{obj.class.to_s})"
|
269
|
-
if !obj.instance_variable_defined?("@flexmock_proxy") || obj.instance_variable_get("@flexmock_proxy").nil?
|
270
|
-
mock = FlexMock.new(name, container)
|
271
|
-
proxy = PartialMockProxy.new(obj, mock, safe_mode)
|
272
|
-
obj.instance_variable_set("@flexmock_proxy", proxy)
|
273
|
-
end
|
274
|
-
obj.instance_variable_get("@flexmock_proxy")
|
146
|
+
class ExtensionRegistry
|
147
|
+
def add_extension(extension)
|
148
|
+
extensions << extension
|
275
149
|
end
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
# Build the chain of mocks for demeter style mocking.
|
280
|
-
#
|
281
|
-
# Warning: Nasty code ahead.
|
282
|
-
#
|
283
|
-
# This method builds a chain of mocks to support demeter style
|
284
|
-
# mocking. Given a mock chain of "first.second.third.last", we
|
285
|
-
# must build a chain of mock methods that return the next mock in
|
286
|
-
# the chain. The expectation for the last method of the chain is
|
287
|
-
# returned as the result of the method.
|
288
|
-
#
|
289
|
-
# Things to consider:
|
290
|
-
#
|
291
|
-
# (1) The expectation for the "first" method must be created by
|
292
|
-
# the proper mechanism, which is supplied by the block parameter
|
293
|
-
# "block". In other words, first expectation is created by
|
294
|
-
# calling the block. (This allows us to create expectations on
|
295
|
-
# both pure mocks and partial mocks, with the block handling the
|
296
|
-
# details).
|
297
|
-
#
|
298
|
-
# (2) Although the first mock is arbitrary, the remaining mocks in
|
299
|
-
# the chain will always be pure mocks created specifically for
|
300
|
-
# this purpose.
|
301
|
-
#
|
302
|
-
# (3) The expectations for all methods but the last in the chain
|
303
|
-
# will be setup to expect no parameters and to return the next
|
304
|
-
# mock in the chain.
|
305
|
-
#
|
306
|
-
# (4) It could very well be the case that several demeter chains
|
307
|
-
# will be defined on a single mock object, and those chains could
|
308
|
-
# share some of the same methods (e.g. "mock.one.two.read" and
|
309
|
-
# "mock.one.two.write" both share the methods "one" and "two").
|
310
|
-
# It is important that the shared methods return the same mocks in
|
311
|
-
# both chains.
|
312
|
-
#
|
313
|
-
def build_demeter_chain(mock, arg, &block)
|
314
|
-
container = mock.flexmock_container
|
315
|
-
names = arg.to_s.split('.')
|
316
|
-
check_method_names(names)
|
317
|
-
exp = nil
|
318
|
-
next_exp = lambda { |n| block.call(n) }
|
319
|
-
loop do
|
320
|
-
method_name = names.shift.to_sym
|
321
|
-
exp = mock.flexmock_find_expectation(method_name)
|
322
|
-
need_new_exp = exp.nil? || names.empty?
|
323
|
-
exp = next_exp.call(method_name) if need_new_exp
|
324
|
-
break if names.empty?
|
325
|
-
if need_new_exp
|
326
|
-
mock = container.flexmock("demeter_#{method_name}")
|
327
|
-
exp.with_no_args.and_return(mock)
|
328
|
-
else
|
329
|
-
mock = exp._return_value([])
|
330
|
-
end
|
331
|
-
check_proper_mock(mock, method_name)
|
332
|
-
next_exp = lambda { |n| mock.should_receive(n) }
|
333
|
-
end
|
334
|
-
exp
|
335
|
-
end
|
336
|
-
|
337
|
-
# Check that the given mock is a real FlexMock mock.
|
338
|
-
def check_proper_mock(mock, method_name)
|
339
|
-
unless mock.kind_of?(FlexMock)
|
340
|
-
fail FlexMock::UsageError,
|
341
|
-
"Conflicting mock declaration for '#{method_name}' in demeter style mock"
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
METHOD_NAME_RE = /^([A-Za-z_][A-Za-z0-9_]*[=!?]?|\[\]=?||\*\*|<<|>>|<=>|[<>=!]=|[=!]~|===|[-+]@|[-+\*\/%&^|<>~`!])$/
|
346
|
-
|
347
|
-
# Check that all the names in the list are valid method names.
|
348
|
-
def check_method_names(names)
|
349
|
-
names.each do |name|
|
350
|
-
fail FlexMock::UsageError, "Ill-formed method name '#{name}'" if
|
351
|
-
name !~ METHOD_NAME_RE
|
352
|
-
end
|
150
|
+
def extensions
|
151
|
+
@extensions ||= []
|
353
152
|
end
|
354
153
|
end
|
355
154
|
|
356
|
-
|
155
|
+
CONTAINER_HELPER = ExtensionRegistry.new
|
357
156
|
end
|
data/lib/flexmock/noop.rb
CHANGED
data/lib/flexmock/ordering.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
#---
|
4
|
-
# Copyright 2003-
|
4
|
+
# Copyright 2003-2013 by Jim Weirich (jim.weirich@gmail.com).
|
5
5
|
# All rights reserved.
|
6
6
|
|
7
7
|
# Permission is granted for use, copying, modification, distribution,
|
@@ -11,7 +11,6 @@
|
|
11
11
|
|
12
12
|
class FlexMock
|
13
13
|
|
14
|
-
# #################################################################
|
15
14
|
# The ordering module contains the methods and data structures used
|
16
15
|
# to determine proper orderring of mocked calls. By providing the
|
17
16
|
# functionality in a module, a individual mock object can order its
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
#---
|
4
|
-
# Copyright 2003-
|
4
|
+
# Copyright 2003-2013 by Jim Weirich (jim.weirich@gmail.com).
|
5
5
|
# All rights reserved.
|
6
6
|
|
7
7
|
# Permission is granted for use, copying, modification, distribution,
|
@@ -10,24 +10,41 @@
|
|
10
10
|
#+++
|
11
11
|
|
12
12
|
require 'flexmock/noop'
|
13
|
+
require 'flexmock/expectation_builder'
|
13
14
|
|
14
15
|
class FlexMock
|
15
16
|
|
16
|
-
# #########################################################################
|
17
17
|
# PartialMockProxy is used to mate the mock framework to an existing
|
18
|
-
# object.
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
18
|
+
# object. The object is "enhanced" with a reference to a mock object
|
19
|
+
# (stored in <tt>@flexmock_proxy</tt>). When the +should_receive+
|
20
|
+
# method is sent to the proxy, it overrides the existing object's
|
21
|
+
# method by creating singleton method that forwards to the mock.
|
22
|
+
# When testing is complete, PartialMockProxy will erase the mocking
|
23
|
+
# infrastructure from the object being mocked (e.g. remove instance
|
24
|
+
# variables and mock singleton methods).
|
25
25
|
#
|
26
26
|
class PartialMockProxy
|
27
27
|
include Ordering
|
28
28
|
|
29
29
|
attr_reader :mock
|
30
30
|
|
31
|
+
# Make a partial mock proxy and install it on the target +obj+.
|
32
|
+
def self.make_proxy_for(obj, container, name, safe_mode)
|
33
|
+
name ||= "flexmock(#{obj.class.to_s})"
|
34
|
+
if ! proxy_defined_on?(obj)
|
35
|
+
mock = FlexMock.new(name, container)
|
36
|
+
proxy = PartialMockProxy.new(obj, mock, safe_mode)
|
37
|
+
obj.instance_variable_set("@flexmock_proxy", proxy)
|
38
|
+
end
|
39
|
+
obj.instance_variable_get("@flexmock_proxy")
|
40
|
+
end
|
41
|
+
|
42
|
+
# Is there a mock proxy defined on the domain object?
|
43
|
+
def self.proxy_defined_on?(obj)
|
44
|
+
obj.instance_variable_defined?("@flexmock_proxy") &&
|
45
|
+
obj.instance_variable_get("@flexmock_proxy")
|
46
|
+
end
|
47
|
+
|
31
48
|
# The following methods are added to partial mocks so that they
|
32
49
|
# can act like a mock.
|
33
50
|
|
@@ -35,7 +52,7 @@ class FlexMock
|
|
35
52
|
:should_receive, :new_instances,
|
36
53
|
:should_receive_with_location,
|
37
54
|
:flexmock_get, :flexmock_teardown, :flexmock_verify,
|
38
|
-
:flexmock_received?, :flexmock_calls,
|
55
|
+
:flexmock_received?, :flexmock_calls, :flexmock_find_expectation
|
39
56
|
]
|
40
57
|
|
41
58
|
# Initialize a PartialMockProxy object.
|
@@ -85,25 +102,27 @@ class FlexMock
|
|
85
102
|
end
|
86
103
|
|
87
104
|
def flexmock_define_expectation(location, *args)
|
88
|
-
|
89
|
-
unless @methods_proxied.include?(
|
90
|
-
hide_existing_method(
|
105
|
+
EXP_BUILDER.parse_should_args(self, args) do |method_name|
|
106
|
+
unless @methods_proxied.include?(method_name)
|
107
|
+
hide_existing_method(method_name)
|
91
108
|
end
|
92
|
-
ex = @mock.flexmock_define_expectation(location,
|
109
|
+
ex = @mock.flexmock_define_expectation(location, method_name)
|
93
110
|
ex.mock = self
|
94
111
|
ex
|
95
112
|
end
|
96
113
|
end
|
97
114
|
|
115
|
+
def flexmock_find_expectation(*args)
|
116
|
+
@mock.flexmock_find_expectation(*args)
|
117
|
+
end
|
118
|
+
|
98
119
|
def add_mock_method(method_name)
|
99
120
|
stow_existing_definition(method_name)
|
100
|
-
|
121
|
+
target_class_eval do
|
101
122
|
define_method(method_name) { |*args, &block|
|
102
|
-
proxy = instance_variable_get("@flexmock_proxy")
|
103
|
-
if proxy.nil?
|
123
|
+
proxy = instance_variable_get("@flexmock_proxy") or
|
104
124
|
fail "Missing FlexMock proxy " +
|
105
|
-
|
106
|
-
end
|
125
|
+
"(for method_name=#{method_name.inspect}, self=\#{self})"
|
107
126
|
proxy.send(method_name, *args, &block)
|
108
127
|
}
|
109
128
|
end
|
@@ -128,39 +147,43 @@ class FlexMock
|
|
128
147
|
# flexmock(ClassName).new_instances(:make).should_receive(...)
|
129
148
|
#
|
130
149
|
def new_instances(*allocators, &block)
|
131
|
-
fail ArgumentError, "new_instances requires a Class to stub" unless
|
150
|
+
fail ArgumentError, "new_instances requires a Class to stub" unless
|
151
|
+
Class === @obj
|
132
152
|
location = caller.first
|
133
153
|
allocators = [:new] if allocators.empty?
|
134
|
-
|
154
|
+
expectation_recorder = ExpectationRecorder.new
|
135
155
|
allocators.each do |allocate_method|
|
136
156
|
check_allocate_method(allocate_method)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
# versions.
|
141
|
-
lambda { }
|
142
|
-
self.flexmock_define_expectation(location, allocate_method).and_return { |*args|
|
143
|
-
new_obj = invoke_original(allocate_method, args)
|
144
|
-
mock = flexmock_container.flexmock(new_obj)
|
145
|
-
block.call(mock) if block_given?
|
146
|
-
result.apply(mock)
|
147
|
-
new_obj
|
157
|
+
flexmock_define_expectation(location, allocate_method).and_return { |*args|
|
158
|
+
create_new_mocked_object(
|
159
|
+
allocate_method, args, expectation_recorder, block)
|
148
160
|
}
|
149
161
|
end
|
150
|
-
|
162
|
+
expectation_recorder
|
163
|
+
end
|
164
|
+
|
165
|
+
# Create a new mocked object.
|
166
|
+
#
|
167
|
+
# The mocked object is created using the following steps:
|
168
|
+
# (1) Allocate with the original allocation method (and args)
|
169
|
+
# (2) Pass to the block for custom configuration.
|
170
|
+
# (3) Apply any recorded expecations
|
171
|
+
#
|
172
|
+
def create_new_mocked_object(allocate_method, args, recorder, block)
|
173
|
+
new_obj = flexmock_invoke_original(allocate_method, args)
|
174
|
+
mock = flexmock_container.flexmock(new_obj)
|
175
|
+
block.call(mock) unless block.nil?
|
176
|
+
recorder.apply(mock)
|
177
|
+
new_obj
|
151
178
|
end
|
179
|
+
private :create_new_mocked_object
|
152
180
|
|
153
181
|
# Invoke the original definition of method on the object supported by
|
154
182
|
# the stub.
|
155
|
-
def
|
183
|
+
def flexmock_invoke_original(method, args)
|
156
184
|
method_proc = @method_definitions[method]
|
157
185
|
method_proc.call(*args)
|
158
186
|
end
|
159
|
-
private :invoke_original
|
160
|
-
|
161
|
-
def flexmock_invoke_original(method, args)
|
162
|
-
invoke_original(method, args)
|
163
|
-
end
|
164
187
|
|
165
188
|
# Verify that the mock has been properly called. After verification,
|
166
189
|
# detach the mocking infrastructure from the existing object.
|
@@ -214,15 +237,22 @@ class FlexMock
|
|
214
237
|
|
215
238
|
def check_allocate_method(allocate_method)
|
216
239
|
if allocate_method == :allocate && RUBY_VERSION >= "1.9"
|
217
|
-
fail UsageError,
|
240
|
+
fail UsageError,
|
241
|
+
"Cannot mock the allocation method using new_instances in Ruby 1.9"
|
218
242
|
end
|
219
243
|
end
|
220
244
|
|
221
245
|
# The singleton class of the object.
|
222
|
-
def
|
246
|
+
def target_singleton_class
|
223
247
|
class << @obj; self; end
|
224
248
|
end
|
225
249
|
|
250
|
+
# Evaluate a block (or string) in the context of the singleton
|
251
|
+
# class of the target partial object.
|
252
|
+
def target_class_eval(*args, &block)
|
253
|
+
target_singleton_class.class_eval(*args, &block)
|
254
|
+
end
|
255
|
+
|
226
256
|
def singleton?(method_name)
|
227
257
|
@obj.flexmock_singleton_defined?(method_name)
|
228
258
|
end
|
@@ -244,19 +274,25 @@ class FlexMock
|
|
244
274
|
@methods_proxied << method_name
|
245
275
|
new_alias = create_alias_for_existing_method(method_name)
|
246
276
|
if new_alias
|
247
|
-
|
248
|
-
@method_definitions[method_name] = Proc.new { |*args|
|
249
|
-
block = nil
|
250
|
-
if Proc === args.last
|
251
|
-
block = args.last
|
252
|
-
args = args[0...-1]
|
253
|
-
end
|
254
|
-
my_object.send(new_alias, *args, &block)
|
255
|
-
}
|
277
|
+
@method_definitions[method_name] = create_aliased_definition(@obj, new_alias)
|
256
278
|
end
|
257
279
|
remove_current_method(method_name) if singleton?(method_name)
|
258
280
|
end
|
259
281
|
|
282
|
+
# Create a method definition that invokes the original behavior
|
283
|
+
# via the alias.
|
284
|
+
def create_aliased_definition(my_object, new_alias)
|
285
|
+
Proc.new { |*args|
|
286
|
+
block = nil
|
287
|
+
if Proc === args.last
|
288
|
+
block = args.last
|
289
|
+
args = args[0...-1]
|
290
|
+
end
|
291
|
+
my_object.send(new_alias, *args, &block)
|
292
|
+
}
|
293
|
+
end
|
294
|
+
private :create_aliased_definition
|
295
|
+
|
260
296
|
# Create an alias for the existing +method_name+. Returns the new
|
261
297
|
# alias name. If the aliasing process fails (because the method
|
262
298
|
# doesn't really exist, then return nil.
|
@@ -273,7 +309,7 @@ class FlexMock
|
|
273
309
|
# meta-programming, so we provide for the case that the
|
274
310
|
# method_name does not exist.
|
275
311
|
def safe_alias_method(new_alias, method_name)
|
276
|
-
|
312
|
+
target_class_eval do
|
277
313
|
begin
|
278
314
|
alias_method(new_alias, method_name)
|
279
315
|
rescue NameError
|
@@ -288,16 +324,18 @@ class FlexMock
|
|
288
324
|
def define_proxy_method(method_name)
|
289
325
|
if method_name.to_s =~ /=$/
|
290
326
|
eval_line = __LINE__ + 1
|
291
|
-
|
327
|
+
target_class_eval %{
|
292
328
|
def #{method_name}(*args, &block)
|
293
|
-
instance_variable_get('@flexmock_proxy').
|
329
|
+
instance_variable_get('@flexmock_proxy').
|
330
|
+
mock.__send__(:#{method_name}, *args, &block)
|
294
331
|
end
|
295
332
|
}, __FILE__, eval_line
|
296
333
|
else
|
297
334
|
eval_line = __LINE__ + 1
|
298
|
-
|
335
|
+
target_class_eval %{
|
299
336
|
def #{method_name}(*args, &block)
|
300
|
-
instance_variable_get('@flexmock_proxy').
|
337
|
+
instance_variable_get('@flexmock_proxy').
|
338
|
+
mock.#{method_name}(*args, &block)
|
301
339
|
end
|
302
340
|
}, __FILE__, eval_line
|
303
341
|
_ = true # make rcov recognize the above eval is covered
|
@@ -311,7 +349,7 @@ class FlexMock
|
|
311
349
|
method_def = @method_definitions[method_name]
|
312
350
|
if method_def
|
313
351
|
the_alias = new_name(method_name)
|
314
|
-
|
352
|
+
target_class_eval do
|
315
353
|
alias_method(method_name, the_alias)
|
316
354
|
end
|
317
355
|
end
|
@@ -324,7 +362,7 @@ class FlexMock
|
|
324
362
|
# Remove the current method if it is a singleton method of the
|
325
363
|
# object being mocked.
|
326
364
|
def remove_current_method(method_name)
|
327
|
-
|
365
|
+
target_class_eval { remove_method(method_name) }
|
328
366
|
end
|
329
367
|
|
330
368
|
# Have we been detached from the existing object?
|