flexmock 0.3.2 → 0.4.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.
- data/CHANGELOG +7 -1
- data/README +90 -17
- data/Rakefile +6 -4
- data/doc/releases/flexmock-0.4.0.rdoc +99 -0
- data/lib/flexmock.rb +202 -37
- data/test/test_class_interception.rb +9 -0
- data/test/test_example.rb +9 -0
- data/test/test_mock.rb +11 -2
- data/test/test_naming.rb +9 -0
- data/test/test_record_mode.rb +9 -0
- data/test/test_samples.rb +1 -1
- data/test/test_should_receive.rb +20 -0
- data/test/test_stubbing.rb +131 -0
- data/test/test_tu_integration.rb +26 -0
- metadata +7 -4
data/CHANGELOG
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
= Changes for FlexMock
|
2
2
|
|
3
|
+
== Pre Version 0.3.3
|
4
|
+
|
5
|
+
* Added stubbing for mocking methods of existing objects.
|
6
|
+
* Added multiple return values for the +and_returns+ method.
|
7
|
+
* Added block initialization to the flexmock method.
|
8
|
+
|
3
9
|
== Version 0.3.2
|
4
10
|
|
5
11
|
* Fixed bug when mock is used as a replacement class in class
|
@@ -7,7 +13,7 @@
|
|
7
13
|
|
8
14
|
== Version 0.3.1
|
9
15
|
|
10
|
-
* Fixed some warnings regarding
|
16
|
+
* Fixed some warnings regarding uninitialized variables.
|
11
17
|
* Added (very) simple class interception.
|
12
18
|
* Added the mock_factory method to go along with class interception.
|
13
19
|
* When using Test::Unit integration, avoid mock verification if the
|
data/README
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
= Flex Mock -- Making Mock Easy
|
2
2
|
|
3
|
-
FlexMock is a simple mock object for unit
|
4
|
-
|
3
|
+
FlexMock is a simple, but flexible, mock object library for Ruby unit
|
4
|
+
testing.
|
5
5
|
|
6
|
-
Version :: 0.
|
6
|
+
Version :: 0.4.0
|
7
7
|
|
8
8
|
= Links
|
9
9
|
|
10
10
|
<b>Documents</b> :: http://onestepback.org/software/flexmock
|
11
|
-
<b>
|
11
|
+
<b>RubyGems</b> :: Install with: <b>gem install flexmock</b>
|
12
|
+
<b>Download</b> :: Download from RubyForge at http://rubyforge.org/frs/?group_id=170
|
12
13
|
|
13
14
|
== Installation
|
14
15
|
|
@@ -25,6 +26,9 @@ mock object that responds to the +read_temperature+ message.
|
|
25
26
|
|
26
27
|
Here's the complete example:
|
27
28
|
|
29
|
+
require 'test/unit'
|
30
|
+
require 'flexmock'
|
31
|
+
|
28
32
|
class TemperatureSampler
|
29
33
|
def initialize(sensor)
|
30
34
|
@sensor = sensor
|
@@ -39,16 +43,15 @@ Here's the complete example:
|
|
39
43
|
class TestTemperatureSampler < Test::Unit::TestCase
|
40
44
|
include FlexMock::TestCase
|
41
45
|
|
42
|
-
def
|
43
|
-
readings = [10, 12, 14]
|
46
|
+
def test_temperature_sampler
|
44
47
|
sensor = flexmock("temp")
|
45
|
-
sensor.should_receive(:read_temperature).and_return
|
48
|
+
sensor.should_receive(:read_temperature).times(3).and_return(10, 12, 14)
|
46
49
|
|
47
50
|
sampler = TemperatureSampler.new(sensor)
|
48
51
|
assert_equal 12, sampler.average_temp
|
49
52
|
|
50
53
|
# NOTE:
|
51
|
-
# all mocks created by the
|
54
|
+
# all mocks created by the flexmock method will be
|
52
55
|
# automatically verified during the test teardown.
|
53
56
|
end
|
54
57
|
end
|
@@ -86,8 +89,19 @@ create the proper expectations on a FlexMock object.
|
|
86
89
|
* <b><tt>with_no_args</tt></b> -- Declares that this expectation
|
87
90
|
matches messages with no arguments
|
88
91
|
|
89
|
-
* <b><tt>
|
90
|
-
will return the given value
|
92
|
+
* <b><tt>and_return(<em>value, ...</em>)</tt></b> -- Declares
|
93
|
+
that the message will return the given value.
|
94
|
+
* If a single value is given, it will be returned for all matching calls.
|
95
|
+
* If multiple values are given, they will be returned in sequence
|
96
|
+
for each successive matching calls. The last value will be
|
97
|
+
repeatably returned if the number of matching calls exceeds the
|
98
|
+
number of values.
|
99
|
+
* If a block is given, its yielded value will be returned. (the
|
100
|
+
block will receive all the arguments given to the mocked method)
|
101
|
+
* The default return value is nil.
|
102
|
+
|
103
|
+
* <b><tt>returns(<em>value, ...</em>)</tt></b> -- Alias for
|
104
|
+
<tt>and_return</tt>.
|
91
105
|
|
92
106
|
* <b><tt>returns { |args| code ... }</tt></b> -- Declares that the
|
93
107
|
message will return whatever the block calculates. The actual
|
@@ -178,7 +192,7 @@ The following rules are used for argument matching:
|
|
178
192
|
with("hello") will match f("hello")
|
179
193
|
|
180
194
|
* If you wish to override the default matching behavior and force
|
181
|
-
matching by equality, you can use
|
195
|
+
matching by equality, you can use the FlexMock.eq convenience
|
182
196
|
method. This is mostly used when you wish to match class objects,
|
183
197
|
since the default matching behavior for class objects is to match
|
184
198
|
instances, not themselves.
|
@@ -215,11 +229,71 @@ The following rules are used for argument matching:
|
|
215
229
|
|
216
230
|
will match any even integer.
|
217
231
|
|
232
|
+
=== Stubbing Behavior in Existing Objects
|
233
|
+
|
234
|
+
Sometimes it is useful to mock the behavior of one or two methods in an
|
235
|
+
existing object without changing the behavior of the rest of the object. By
|
236
|
+
using the +flexstub+ method, tests can now do exactly that.
|
237
|
+
|
238
|
+
For example, suppose that a Dog object uses a Woofer object to
|
239
|
+
bark. The code for Dog looks like this (we will leave the code for
|
240
|
+
Woofer to your imagination):
|
241
|
+
|
242
|
+
class Dog
|
243
|
+
def initialize
|
244
|
+
@woofer = Woofer.new
|
245
|
+
end
|
246
|
+
def bark
|
247
|
+
@woofer.woof
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
Now we want to test Dog, but using a real Woofer object in the test is
|
252
|
+
a real pain (why? ... well because Woofer plays a sound file of a dog
|
253
|
+
barking, and that's really annoying during testing).
|
254
|
+
|
255
|
+
So, how can we create a Dog object with mocked Woofer? All we need to
|
256
|
+
do is stub out the +new+ method of the Woofer class object and tell to
|
257
|
+
to return anything we want.
|
258
|
+
|
259
|
+
Here's the test code:
|
260
|
+
|
261
|
+
class TestDogBarking < Test::Unit::TestCase
|
262
|
+
include FlexMock::TestCase
|
263
|
+
|
264
|
+
# Setup the tests by stubbing the +new+ method of
|
265
|
+
# Woofer and return a mock woofer.
|
266
|
+
def setup
|
267
|
+
flexstub(Woofer).should_receive(:new).and_return {
|
268
|
+
flexmock("woofer") do |mock|
|
269
|
+
mock.should_receive(:woof).and_return(:grrrr)
|
270
|
+
end
|
271
|
+
}
|
272
|
+
end
|
273
|
+
|
274
|
+
def test_bark
|
275
|
+
assert_equal :grrrr, @dog.bark
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
The nice thing about stub is that after the test is over, the stubbed
|
280
|
+
out methods are returned to their normal state. Outside the test
|
281
|
+
everything is back to normal.
|
282
|
+
|
283
|
+
The stub technique was inspired by the +Stuba+ library in the +Mocha+
|
284
|
+
project.
|
285
|
+
|
218
286
|
=== Class Interception
|
219
287
|
|
220
|
-
|
221
|
-
|
222
|
-
|
288
|
+
<b>NOTE:</b> ::
|
289
|
+
<em>Class Interception is now deprecated. It only worked in a
|
290
|
+
small number of cases. See the "Mocking Existing Objects"
|
291
|
+
example above for a much better approach to the same problem.
|
292
|
+
The Class Interception API will probably disappear in a future
|
293
|
+
version of FlexMock.</em>
|
294
|
+
|
295
|
+
FlexMock now supports simple class interception. For the duration of a test, a
|
296
|
+
mock class take the place of a named class inside the class to be tested.
|
223
297
|
|
224
298
|
Example:
|
225
299
|
|
@@ -340,8 +414,8 @@ effect.
|
|
340
414
|
|
341
415
|
def test_multiple_gets
|
342
416
|
file = flexmock('file')
|
343
|
-
|
344
|
-
|
417
|
+
file.should_receive(:gets).with_no_args.
|
418
|
+
and_return("line 1\n", "line 2\n")
|
345
419
|
# test code here
|
346
420
|
end
|
347
421
|
|
@@ -386,7 +460,6 @@ Permission is granted for use, copying, modification, distribution,
|
|
386
460
|
and distribution of modified versions of this work as long as the
|
387
461
|
above copyright notice is included.
|
388
462
|
|
389
|
-
|
390
463
|
= Other stuff
|
391
464
|
|
392
465
|
Author:: Jim Weirich <jim@weirichhouse.org>
|
data/Rakefile
CHANGED
@@ -6,9 +6,10 @@ require 'rake/clean'
|
|
6
6
|
require 'rake/rdoctask'
|
7
7
|
require 'rake/testtask'
|
8
8
|
|
9
|
+
CLEAN.include('*.tmp')
|
9
10
|
CLOBBER.include("html", 'pkg')
|
10
11
|
|
11
|
-
PKG_VERSION = '0.
|
12
|
+
PKG_VERSION = '0.4.0'
|
12
13
|
|
13
14
|
PKG_FILES = FileList[
|
14
15
|
'[A-Z]*',
|
@@ -23,7 +24,6 @@ RDOC_FILES = FileList[
|
|
23
24
|
'CHANGELOG',
|
24
25
|
'lib/**/*.rb',
|
25
26
|
'doc/**/*.rdoc',
|
26
|
-
'test/*.rb'
|
27
27
|
]
|
28
28
|
|
29
29
|
|
@@ -47,8 +47,7 @@ rd = Rake::RDocTask.new("rdoc") do |rdoc|
|
|
47
47
|
# rdoc.template = 'css2'
|
48
48
|
rdoc.title = "Flex Mock"
|
49
49
|
rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
50
|
-
rdoc.rdoc_files.include(
|
51
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
50
|
+
rdoc.rdoc_files.include(RDOC_FILES)
|
52
51
|
end
|
53
52
|
|
54
53
|
task :rdoc => ["README"]
|
@@ -148,3 +147,6 @@ task :tag do
|
|
148
147
|
sh %{svn copy #{SVNHOME}/trunk #{SVNHOME}/tags/rel-#{PKG_VERSION} -m 'Release #{PKG_VERSION}'}
|
149
148
|
end
|
150
149
|
|
150
|
+
task :dbg do
|
151
|
+
FileList['**/*.rb'].egrep(/DBG/)
|
152
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
= FlexMock 0.4.0 Released
|
2
|
+
|
3
|
+
FlexMock is a flexible mocking library for use in Ruby's Test::Unit
|
4
|
+
test framework. Version 0.4.0 enhances FlexMock with the ability to
|
5
|
+
stub and mock methods in existing objects.
|
6
|
+
|
7
|
+
== Changes
|
8
|
+
|
9
|
+
=== New Features in 0.4.0
|
10
|
+
|
11
|
+
* You can now use the flexstub(object) method to mock or stub
|
12
|
+
individual methods in an existing object. The original definitions
|
13
|
+
of the methods are restored at the end of a test.
|
14
|
+
|
15
|
+
* The and_return (and its alias, returns) now accepts a list of
|
16
|
+
arguments and will return the values in the list one at a time for
|
17
|
+
each successive call to the mocked method.
|
18
|
+
|
19
|
+
* The flexmock() method now accepts an initialization block so that a
|
20
|
+
mock can be created and configured in one step without using a local
|
21
|
+
variable. This is really handy when mocking factory or creation
|
22
|
+
methods that in turn return a mock.
|
23
|
+
|
24
|
+
== What is FlexMock?
|
25
|
+
|
26
|
+
FlexMock is a flexible Ruby mocking library that works with Ruby's
|
27
|
+
Test::Unit framework to create easy to use mocks.
|
28
|
+
|
29
|
+
=== Features
|
30
|
+
|
31
|
+
* Easy integration with Test::Unit. Mocks created with the flexmock
|
32
|
+
method are automatically verified at the end of the test.
|
33
|
+
|
34
|
+
* A fluent interface that allows mock behavior to be specified very
|
35
|
+
easily.
|
36
|
+
|
37
|
+
* A "record mode" where an existing implementation can record its
|
38
|
+
interaction with a mock for later validation against a new
|
39
|
+
implementation.
|
40
|
+
|
41
|
+
* Easy mocking of individual methods in existing, non-mock objects.
|
42
|
+
|
43
|
+
=== Example
|
44
|
+
|
45
|
+
Suppose you had a Dog object that wagged a tail when it was happy.
|
46
|
+
Something like this:
|
47
|
+
|
48
|
+
class Dog
|
49
|
+
def initialize(a_tail)
|
50
|
+
@tail = a_tail
|
51
|
+
end
|
52
|
+
def happy
|
53
|
+
@tail.wag
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
To test the +Dog+ class without a real +Tail+ object (perhaps because
|
58
|
+
real +Tail+ objects activate servos in some robotic equipment), you
|
59
|
+
can do something like this:
|
60
|
+
|
61
|
+
require 'test/unit'
|
62
|
+
require 'flexmock'
|
63
|
+
|
64
|
+
class TestDog < Test::Unit::TestCase
|
65
|
+
include FlexMock::TestCase
|
66
|
+
|
67
|
+
def test_dog_wags_tail_when_happy
|
68
|
+
tail = flexmock("tail")
|
69
|
+
tail.should_receive(:wag).once
|
70
|
+
dog = Dog.new(tail)
|
71
|
+
dog.happy
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
FlexMock will automatically verify that the mocked tail object
|
76
|
+
received the message +wag+ exactly one time. If it doesn't, the test
|
77
|
+
will not pass.
|
78
|
+
|
79
|
+
See the FlexMock documentation at
|
80
|
+
http://onestepback.org/software/flexmock for details on specifying
|
81
|
+
arguments and return values on mocked methods, as well as a simple
|
82
|
+
technique for mocking tail objects when the Dog class creates the tail
|
83
|
+
objects directly.
|
84
|
+
|
85
|
+
== Availability
|
86
|
+
|
87
|
+
FlexMock is distributed with Rails, or you can make sure you have the
|
88
|
+
latest version with a quick RubyGems command:
|
89
|
+
|
90
|
+
gem install flexmock (you may need root/admin privileges)
|
91
|
+
|
92
|
+
Otherwise, you can get it from the more traditional places:
|
93
|
+
|
94
|
+
Download:: http://rubyforge.org/project/showfiles.php?group_id=170
|
95
|
+
|
96
|
+
You will find documentation at:
|
97
|
+
http://onestepback.org/software/flexmock/
|
98
|
+
|
99
|
+
-- Jim Weirich
|
data/lib/flexmock.rb
CHANGED
@@ -63,6 +63,7 @@ class FlexMock
|
|
63
63
|
@mock_current_order = 0
|
64
64
|
@mock_groups = {}
|
65
65
|
@ignore_missing = false
|
66
|
+
@verified = false
|
66
67
|
end
|
67
68
|
|
68
69
|
# Handle all messages denoted by +sym+ by calling the given block
|
@@ -77,13 +78,19 @@ class FlexMock
|
|
77
78
|
# Verify that each method that had an explicit expected count was
|
78
79
|
# actually called that many times.
|
79
80
|
def mock_verify
|
81
|
+
return if @verified
|
82
|
+
@verified = true
|
80
83
|
mock_wrap do
|
81
84
|
@expectations.each do |sym, handler|
|
82
|
-
|
85
|
+
handler.mock_verify
|
83
86
|
end
|
84
87
|
end
|
85
88
|
end
|
86
89
|
|
90
|
+
# Teardown and infrastructure setup for this mock.
|
91
|
+
def mock_teardown
|
92
|
+
end
|
93
|
+
|
87
94
|
# Allocation a new order number from the mock.
|
88
95
|
def mock_allocate_order
|
89
96
|
@auto_allocate = true
|
@@ -100,10 +107,10 @@ class FlexMock
|
|
100
107
|
def method_missing(sym, *args, &block)
|
101
108
|
mock_wrap do
|
102
109
|
if handler = @expectations[sym]
|
103
|
-
|
104
|
-
|
110
|
+
args << block if block_given?
|
111
|
+
handler.call(*args)
|
105
112
|
else
|
106
|
-
|
113
|
+
super(sym, *args, &block) unless @ignore_missing
|
107
114
|
end
|
108
115
|
end
|
109
116
|
end
|
@@ -179,7 +186,7 @@ class FlexMock
|
|
179
186
|
# end # mock is verified here
|
180
187
|
#
|
181
188
|
# NOTE: If you include FlexMock::TestCase into your test case
|
182
|
-
# file, you can create
|
189
|
+
# file, you can create mocks that will be automatically verified in
|
183
190
|
# the test teardown by using the +flexmock+ method.
|
184
191
|
#
|
185
192
|
def use(*names)
|
@@ -192,7 +199,7 @@ class FlexMock
|
|
192
199
|
raise
|
193
200
|
ensure
|
194
201
|
mocks.each do |mock|
|
195
|
-
|
202
|
+
mock.mock_verify unless got_exception
|
196
203
|
end
|
197
204
|
end
|
198
205
|
|
@@ -200,9 +207,9 @@ class FlexMock
|
|
200
207
|
# looking string.
|
201
208
|
def format_args(sym, args)
|
202
209
|
if args
|
203
|
-
|
210
|
+
"#{sym}(#{args.collect { |a| a.inspect }.join(', ')})"
|
204
211
|
else
|
205
|
-
|
212
|
+
"#{sym}(*args)"
|
206
213
|
end
|
207
214
|
end
|
208
215
|
|
@@ -263,16 +270,16 @@ class FlexMock
|
|
263
270
|
#
|
264
271
|
# First, look for an expectation that matches the arguements and
|
265
272
|
# is eligible to be called. Failing that, look for a expectation
|
266
|
-
# that matches
|
273
|
+
# that matches the arguments (at this point it will be ineligible,
|
267
274
|
# but at least we will get a good failure message). Finally,
|
268
275
|
# check for expectations that don't have any argument matching
|
269
276
|
# criteria.
|
270
277
|
def call(*args)
|
271
278
|
exp = @expectations.find { |e| e.match_args(args) && e.eligible? } ||
|
272
279
|
@expectations.find { |e| e.match_args(args) } ||
|
273
|
-
|
280
|
+
@expectations.find { |e| e.expected_args.nil? }
|
274
281
|
FlexMock.check("no matching handler found for " +
|
275
|
-
|
282
|
+
FlexMock.format_args(@sym, args)) { ! exp.nil? }
|
276
283
|
exp.verify_call(*args)
|
277
284
|
end
|
278
285
|
|
@@ -290,7 +297,7 @@ class FlexMock
|
|
290
297
|
# expectations.
|
291
298
|
def mock_verify
|
292
299
|
@expectations.each do |exp|
|
293
|
-
|
300
|
+
exp.mock_verify
|
294
301
|
end
|
295
302
|
end
|
296
303
|
end
|
@@ -388,7 +395,7 @@ class FlexMock
|
|
388
395
|
# times.
|
389
396
|
def validate(n)
|
390
397
|
assert_equal @limit, n,
|
391
|
-
|
398
|
+
"method '#{@exp}' called incorrect number of times"
|
392
399
|
end
|
393
400
|
end
|
394
401
|
|
@@ -400,8 +407,8 @@ class FlexMock
|
|
400
407
|
# times.
|
401
408
|
def validate(n)
|
402
409
|
assert n >= @limit,
|
403
|
-
|
404
|
-
|
410
|
+
"Method '#{@exp}' should be called at least #{@limit} times,\n" +
|
411
|
+
"only called #{n} times"
|
405
412
|
end
|
406
413
|
|
407
414
|
# If the expectation has been called +n+ times, is it still
|
@@ -420,8 +427,8 @@ class FlexMock
|
|
420
427
|
# Validate the method expectation was called at least +n+ times.
|
421
428
|
def validate(n)
|
422
429
|
assert n <= @limit,
|
423
|
-
|
424
|
-
|
430
|
+
"Method '#{@exp}' should be called at most #{@limit} times,\n" +
|
431
|
+
"only called #{n} times"
|
425
432
|
end
|
426
433
|
end
|
427
434
|
|
@@ -477,8 +484,8 @@ class FlexMock
|
|
477
484
|
def validate_order
|
478
485
|
return if @order_number.nil?
|
479
486
|
FlexMock.check("method #{to_s} called out of order " +
|
480
|
-
|
481
|
-
|
487
|
+
"(expected order #{@order_number}, was #{@mock.mock_current_order})") {
|
488
|
+
@order_number >= @mock.mock_current_order
|
482
489
|
}
|
483
490
|
@mock.mock_current_order = @order_number
|
484
491
|
end
|
@@ -488,7 +495,7 @@ class FlexMock
|
|
488
495
|
# the teardown process.
|
489
496
|
def mock_verify
|
490
497
|
@count_validators.each do |v|
|
491
|
-
|
498
|
+
v.validate(@actual_count)
|
492
499
|
end
|
493
500
|
end
|
494
501
|
|
@@ -503,8 +510,8 @@ class FlexMock
|
|
503
510
|
# Does the expected argument match the corresponding actual value.
|
504
511
|
def match_arg(expected, actual)
|
505
512
|
expected === actual ||
|
506
|
-
|
507
|
-
|
513
|
+
expected == actual ||
|
514
|
+
( Regexp === expected && expected === actual.to_s )
|
508
515
|
end
|
509
516
|
|
510
517
|
# Declare that the method should expect the given argument list.
|
@@ -526,10 +533,17 @@ class FlexMock
|
|
526
533
|
end
|
527
534
|
|
528
535
|
# Declare that the method returns a particular value (when the
|
529
|
-
# argument list is matched).
|
530
|
-
# on each call and its value is returned. +and_return+ is an
|
531
|
-
# alias for +returns+.
|
536
|
+
# argument list is matched).
|
532
537
|
#
|
538
|
+
# * If a single value is given, it will be returned for all matching
|
539
|
+
# calls.
|
540
|
+
# * If multiple values are given, each value will be returned in turn for
|
541
|
+
# each successive call. If the number of matching calls is greater
|
542
|
+
# than the number of values, the last value will be returned for
|
543
|
+
# the extra matching calls.
|
544
|
+
# * If a block is given, it is evaluated on each call and its
|
545
|
+
# value is returned.
|
546
|
+
#
|
533
547
|
# For example:
|
534
548
|
#
|
535
549
|
# mock.should_receive(:f).returns(12) # returns 12
|
@@ -537,11 +551,15 @@ class FlexMock
|
|
537
551
|
# mock.should_receive(:f).with(String). # returns an
|
538
552
|
# returns { |str| str.upcase } # upcased string
|
539
553
|
#
|
540
|
-
|
541
|
-
|
554
|
+
# +and_return+ is an alias for +returns+.
|
555
|
+
#
|
556
|
+
def returns(*args, &block)
|
557
|
+
@return_block = block_given? ?
|
558
|
+
block :
|
559
|
+
lambda { args.size == 1 ? args.first : args.shift }
|
542
560
|
self
|
543
561
|
end
|
544
|
-
alias :and_return :returns
|
562
|
+
alias :and_return :returns # :nodoc:
|
545
563
|
|
546
564
|
# Declare that the method may be called any number of times.
|
547
565
|
def zero_or_more_times
|
@@ -630,12 +648,12 @@ class FlexMock
|
|
630
648
|
#
|
631
649
|
def ordered(group_name=nil)
|
632
650
|
if group_name.nil?
|
633
|
-
|
651
|
+
@order_number = @mock.mock_allocate_order
|
634
652
|
elsif (num = @mock.mock_groups[group_name])
|
635
|
-
|
653
|
+
@order_number = num
|
636
654
|
else
|
637
|
-
|
638
|
-
|
655
|
+
@order_number = @mock.mock_allocate_order
|
656
|
+
@mock.mock_groups[group_name] = @order_number
|
639
657
|
end
|
640
658
|
self
|
641
659
|
end
|
@@ -727,6 +745,10 @@ class FlexMock
|
|
727
745
|
m.mock_verify
|
728
746
|
end
|
729
747
|
end
|
748
|
+
ensure
|
749
|
+
@flexmock_created_mocks.each do |m|
|
750
|
+
m.mock_teardown
|
751
|
+
end
|
730
752
|
@flexmock_interceptors ||= []
|
731
753
|
@flexmock_interceptors.each do |i|
|
732
754
|
i.restore
|
@@ -736,13 +758,37 @@ class FlexMock
|
|
736
758
|
# Create a FlexMock object with the given name. Mocks created
|
737
759
|
# with this method will be automatically verify during teardown
|
738
760
|
# (assuming the the flexmock teardown isn't overridden).
|
761
|
+
#
|
762
|
+
# If a block is given, then the mock object is passed to the block and
|
763
|
+
# may be configured in the block.
|
739
764
|
def flexmock(name="unknown")
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
765
|
+
mock = FlexMock.new(name)
|
766
|
+
yield(mock) if block_given?
|
767
|
+
flexmock_remember(mock)
|
768
|
+
mock
|
744
769
|
end
|
745
|
-
|
770
|
+
|
771
|
+
# Stub the given object by overriding the behavior of individual methods.
|
772
|
+
# The stub object returned will respond to the +should_receive+
|
773
|
+
# method, just like normal stubs. Singleton methods cannot be
|
774
|
+
# stubbed.
|
775
|
+
#
|
776
|
+
# Example: Stub out DBI to return a fake db connection.
|
777
|
+
#
|
778
|
+
# flexstub(DBI).should_receive(:connect).and_return {
|
779
|
+
# fake_db = flexmock("db connection")
|
780
|
+
# fake_db.should_receive(:select_all).and_return(...)
|
781
|
+
# fake_db
|
782
|
+
# }
|
783
|
+
#
|
784
|
+
def flexstub(obj, name=nil)
|
785
|
+
name ||= "flexstub(#{obj.class.to_s})"
|
786
|
+
obj.instance_eval {
|
787
|
+
@flexmock_proxy ||= StubProxy.new(obj, FlexMock.new(name))
|
788
|
+
}
|
789
|
+
flexmock_remember(obj.instance_variable_get("@flexmock_proxy"))
|
790
|
+
end
|
791
|
+
|
746
792
|
# Intercept the named class in the target class for the duration
|
747
793
|
# of the test. Class interception is very simple-minded and has a
|
748
794
|
# number of restrictions. First, the intercepted class must be
|
@@ -762,6 +808,14 @@ class FlexMock
|
|
762
808
|
@flexmock_interceptors << result
|
763
809
|
result
|
764
810
|
end
|
811
|
+
|
812
|
+
private
|
813
|
+
|
814
|
+
def flexmock_remember(mocking_object)
|
815
|
+
@flexmock_created_mocks ||= []
|
816
|
+
@flexmock_created_mocks << mocking_object
|
817
|
+
mocking_object
|
818
|
+
end
|
765
819
|
end
|
766
820
|
|
767
821
|
####################################################################
|
@@ -880,4 +934,115 @@ class FlexMock
|
|
880
934
|
end
|
881
935
|
end
|
882
936
|
|
937
|
+
####################################################################
|
938
|
+
# StubProxy is used to mate the mock framework to an existing
|
939
|
+
# object. The object is "enhanced" with a reference to a mock
|
940
|
+
# object (stored in <tt>@flexmock_mock</tt>). When the
|
941
|
+
# +should_receive+ method is sent to the proxy, it overrides the
|
942
|
+
# existing object's method by creating singleton method that
|
943
|
+
# forwards to the mock. When testing is complete, StubProxy
|
944
|
+
# will erase the mocking infrastructure from the object being
|
945
|
+
# stubbed (e.g. remove instance variables and mock singleton
|
946
|
+
# methods).
|
947
|
+
#
|
948
|
+
class StubProxy
|
949
|
+
attr_reader :mock
|
950
|
+
|
951
|
+
def initialize(obj, mock)
|
952
|
+
@obj = obj
|
953
|
+
@mock = mock
|
954
|
+
@methods_to_restore = []
|
955
|
+
@method_definitions = {}
|
956
|
+
end
|
957
|
+
|
958
|
+
# Stub out the given method in the existing object and then let the
|
959
|
+
# mock object handle should_receive.
|
960
|
+
def should_receive(method_name)
|
961
|
+
method_name = method_name.to_sym
|
962
|
+
if @obj.methods.include?(method_name.to_s)
|
963
|
+
@methods_to_restore << method_name
|
964
|
+
end
|
965
|
+
hide_existing_method(method_name)
|
966
|
+
@mock.should_receive(method_name)
|
967
|
+
end
|
968
|
+
|
969
|
+
# Verify that the mock has been properly called. After verification,
|
970
|
+
# detach the mocking infrastructure from the existing object.
|
971
|
+
def mock_verify
|
972
|
+
@mock.mock_verify
|
973
|
+
end
|
974
|
+
|
975
|
+
# Remove all traces of the mocking framework from the existing object.
|
976
|
+
def mock_teardown
|
977
|
+
if ! detached?
|
978
|
+
@methods_to_restore.each do |method_name|
|
979
|
+
remove_current_method(method_name)
|
980
|
+
restore_original_definition(method_name) if @method_definitions[method_name]
|
981
|
+
end
|
982
|
+
@obj.instance_variable_set("@flexmock_proxy", nil)
|
983
|
+
@obj = nil
|
984
|
+
end
|
985
|
+
end
|
986
|
+
|
987
|
+
private
|
988
|
+
|
989
|
+
# The singleton class of the object.
|
990
|
+
def sclass
|
991
|
+
class << @obj; self; end
|
992
|
+
end
|
993
|
+
|
994
|
+
# Is the current method a singleton method in the object we are
|
995
|
+
# mocking?
|
996
|
+
def singleton?(method_name)
|
997
|
+
@obj.methods(false).include?(method_name.to_s)
|
998
|
+
end
|
999
|
+
|
1000
|
+
# Hide the existing method definition with a singleton defintion
|
1001
|
+
# that proxies to our mock object. If the current definition is a
|
1002
|
+
# singleton, we need to record the definition and remove it before
|
1003
|
+
# creating our own singleton method. If the current definition is
|
1004
|
+
# not a singleton, all we need to do is override it with our own
|
1005
|
+
# singleton.
|
1006
|
+
def hide_existing_method(method_name)
|
1007
|
+
if singleton?(method_name)
|
1008
|
+
@method_definitions[method_name] = @obj.method(method_name)
|
1009
|
+
remove_current_method(method_name)
|
1010
|
+
end
|
1011
|
+
define_proxy_method(method_name)
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
# Define a proxy method that forwards to our mock object. The
|
1015
|
+
# proxy method is defined as a singleton method on the object
|
1016
|
+
# being mocked.
|
1017
|
+
def define_proxy_method(method_name)
|
1018
|
+
sclass.class_eval %{
|
1019
|
+
def #{method_name}(*args, &block)
|
1020
|
+
@flexmock_proxy.mock.#{method_name}(*args, &block)
|
1021
|
+
end
|
1022
|
+
}
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
# Restore the original singleton defintion for method_name that
|
1026
|
+
# was saved earlier.
|
1027
|
+
def restore_original_definition(method_name)
|
1028
|
+
method_def = @method_definitions[method_name]
|
1029
|
+
if method_def
|
1030
|
+
sclass.class_eval {
|
1031
|
+
define_method(method_name, &method_def)
|
1032
|
+
}
|
1033
|
+
end
|
1034
|
+
end
|
1035
|
+
|
1036
|
+
# Remove the current method if it is a singleton method of the
|
1037
|
+
# object being mocked.
|
1038
|
+
def remove_current_method(method_name)
|
1039
|
+
sclass.class_eval { remove_method(method_name) }
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
# Have we been detached from the existing object?
|
1043
|
+
def detached?
|
1044
|
+
@obj.nil?
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
end
|
883
1048
|
end
|
@@ -1,5 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
#---
|
4
|
+
# Copyright 2006 by Jim Weirich (jweirich@one.net).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#+++
|
11
|
+
|
3
12
|
require 'test/unit'
|
4
13
|
require 'flexmock'
|
5
14
|
|
data/test/test_example.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
#---
|
4
|
+
# Copyright 2006 by Jim Weirich (jweirich@one.net).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#+++
|
11
|
+
|
3
12
|
require 'test/unit'
|
4
13
|
require 'flexmock'
|
5
14
|
|
data/test/test_mock.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
#---
|
4
|
+
# Copyright 2006 by Jim Weirich (jweirich@one.net).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#+++
|
11
|
+
|
3
12
|
require 'test/unit'
|
4
13
|
require 'flexmock'
|
5
14
|
|
@@ -78,8 +87,8 @@ class TestFlexMock < Test::Unit::TestCase
|
|
78
87
|
def test_zero_counts
|
79
88
|
assert_raises(Test::Unit::AssertionFailedError) do
|
80
89
|
FlexMock.use { |m|
|
81
|
-
|
82
|
-
|
90
|
+
m.mock_handle(:blip, 0)
|
91
|
+
m.blip
|
83
92
|
}
|
84
93
|
end
|
85
94
|
end
|
data/test/test_naming.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
#---
|
4
|
+
# Copyright 2006 by Jim Weirich (jweirich@one.net).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#+++
|
11
|
+
|
3
12
|
require 'test/unit'
|
4
13
|
require 'flexmock'
|
5
14
|
|
data/test/test_record_mode.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
#---
|
4
|
+
# Copyright 2006 by Jim Weirich (jweirich@one.net).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#+++
|
11
|
+
|
3
12
|
require 'test/unit'
|
4
13
|
require 'flexmock'
|
5
14
|
|
data/test/test_samples.rb
CHANGED
data/test/test_should_receive.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
#---
|
4
|
+
# Copyright 2006 by Jim Weirich (jweirich@one.net).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#+++
|
11
|
+
|
3
12
|
require 'test/unit'
|
4
13
|
require 'flexmock'
|
5
14
|
|
@@ -21,6 +30,17 @@ class TestFlexMockShoulds < Test::Unit::TestCase
|
|
21
30
|
assert_equal 1, m.hi(123)
|
22
31
|
end
|
23
32
|
end
|
33
|
+
|
34
|
+
def test_returns_with_multiple_values
|
35
|
+
FlexMock.use do |m|
|
36
|
+
m.should_receive(:hi).and_return(1,2,3)
|
37
|
+
assert_equal 1, m.hi
|
38
|
+
assert_equal 2, m.hi
|
39
|
+
assert_equal 3, m.hi
|
40
|
+
assert_equal 3, m.hi
|
41
|
+
assert_equal 3, m.hi
|
42
|
+
end
|
43
|
+
end
|
24
44
|
|
25
45
|
def test_returns_with_block
|
26
46
|
FlexMock.use do |m|
|
@@ -0,0 +1,131 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#---
|
4
|
+
# Copyright 2006 by Jim Weirich (jweirich@one.net).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#+++
|
11
|
+
|
12
|
+
require 'test/unit'
|
13
|
+
require 'flexmock'
|
14
|
+
|
15
|
+
class TestStubbing < Test::Unit::TestCase
|
16
|
+
include FlexMock::TestCase
|
17
|
+
|
18
|
+
class Dog
|
19
|
+
def bark
|
20
|
+
:woof
|
21
|
+
end
|
22
|
+
def Dog.create
|
23
|
+
:new_dog
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_stub_command_add_behavior_to_arbitrary_objects
|
28
|
+
obj = Object.new
|
29
|
+
flexstub(obj).should_receive(:hi).once.and_return(:stub_hi)
|
30
|
+
assert_equal :stub_hi, obj.hi
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_stubbed_methods_can_take_blocks
|
34
|
+
obj = Object.new
|
35
|
+
flexstub(obj).should_receive(:with_block).once.with(Proc).
|
36
|
+
and_return { |block| block.call }
|
37
|
+
assert_equal :block, obj.with_block { :block }
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_multiple_stubs_on_the_same_object_reuse_the_same_stub_proxy
|
41
|
+
obj = Object.new
|
42
|
+
assert_equal flexstub(obj), flexstub(obj)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_multiple_methods_can_be_stubbed
|
46
|
+
dog = Dog.new
|
47
|
+
flexstub(dog).should_receive(:bark).and_return(:grrrr)
|
48
|
+
flexstub(dog).should_receive(:wag).and_return(:happy)
|
49
|
+
assert_equal :grrrr, dog.bark
|
50
|
+
assert_equal :happy, dog.wag
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_original_behavior_can_be_restored
|
54
|
+
dog = Dog.new
|
55
|
+
stub_proxy = flexstub(dog)
|
56
|
+
stub_proxy.should_receive(:bark).once.and_return(:growl)
|
57
|
+
assert_equal :growl, dog.bark
|
58
|
+
stub_proxy.mock_teardown
|
59
|
+
assert_equal :woof, dog.bark
|
60
|
+
assert_equal nil, dog.instance_variable_get("@flexmock_proxy")
|
61
|
+
assert ! dog.methods.include?("flexmock_old_bark")
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_original_missing_behavior_can_be_restored
|
65
|
+
obj = Object.new
|
66
|
+
stub_proxy = flexstub(obj)
|
67
|
+
stub_proxy.should_receive(:hi).once.and_return(:ok)
|
68
|
+
assert_equal :ok, obj.hi
|
69
|
+
stub_proxy.mock_teardown
|
70
|
+
assert_raise(NoMethodError) { obj.hi }
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_original_behavior_is_restored_when_multiple_methods_are_mocked
|
74
|
+
dog = Dog.new
|
75
|
+
flexstub(dog).should_receive(:bark).and_return(:grrrr)
|
76
|
+
flexstub(dog).should_receive(:wag).and_return(:happy)
|
77
|
+
flexstub(dog).mock_teardown
|
78
|
+
assert_equal :woof, dog.bark
|
79
|
+
assert_raise(NoMethodError) { dog.wag }
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_original_behavior_is_restored_on_class_objects
|
83
|
+
flexstub(Dog).should_receive(:create).once.and_return(:new_stub)
|
84
|
+
assert_equal :new_stub, Dog.create
|
85
|
+
flexstub(Dog).mock_teardown
|
86
|
+
assert_equal :new_dog, Dog.create
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_original_behavior_is_restored_on_singleton_methods
|
90
|
+
obj = Object.new
|
91
|
+
def obj.hi() :hello end
|
92
|
+
flexstub(obj).should_receive(:hi).once.and_return(:hola)
|
93
|
+
|
94
|
+
assert_equal :hola, obj.hi
|
95
|
+
flexstub(obj).mock_teardown
|
96
|
+
assert_equal :hello, obj.hi
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_original_behavior_is_restored_even_when_errors
|
100
|
+
flexstub(Dog).should_receive(:create).once.and_return(:mock)
|
101
|
+
flexmock_teardown rescue nil
|
102
|
+
assert_equal :new_dog, Dog.create
|
103
|
+
|
104
|
+
# Now disable the mock so that it doesn't cause errors on normal
|
105
|
+
# test teardown
|
106
|
+
m = flexstub(Dog).mock
|
107
|
+
def m.mock_verify() end
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_mock_is_verified_when_the_stub_is_verified
|
111
|
+
obj = Object.new
|
112
|
+
stub_proxy = flexstub(obj)
|
113
|
+
stub_proxy.should_receive(:hi).once.and_return(:ok)
|
114
|
+
assert_raise(Test::Unit::AssertionFailedError) {
|
115
|
+
stub_proxy.mock_verify
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_stub_can_have_explicit_name
|
120
|
+
obj = Object.new
|
121
|
+
stub_proxy = flexstub(obj, "Charlie")
|
122
|
+
assert_equal "Charlie", stub_proxy.mock.mock_name
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_unamed_stub_will_use_default_naming_convention
|
126
|
+
obj = Object.new
|
127
|
+
stub_proxy = flexstub(obj)
|
128
|
+
assert_equal "flexstub(Object)", stub_proxy.mock.mock_name
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
data/test/test_tu_integration.rb
CHANGED
@@ -1,8 +1,34 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
#---
|
4
|
+
# Copyright 2006 by Jim Weirich (jweirich@one.net).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#+++
|
11
|
+
|
3
12
|
require 'test/unit'
|
4
13
|
require 'flexmock'
|
5
14
|
|
15
|
+
class TestTuIntegrationFlexMockMethod < Test::Unit::TestCase
|
16
|
+
include FlexMock::TestCase
|
17
|
+
|
18
|
+
def test_can_construct_flexmock
|
19
|
+
mock = flexmock("x")
|
20
|
+
mock.should_receive(:hi).and_return(:hello)
|
21
|
+
assert_equal :hello, mock.hi
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_can_construct_flexmock_with_block
|
25
|
+
mock = flexmock("x") do |m|
|
26
|
+
m.should_receive(:hi).and_return(:hello)
|
27
|
+
end
|
28
|
+
assert_equal :hello, mock.hi
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
6
32
|
class TestTuIntegrationMockVerificationInTeardown < Test::Unit::TestCase
|
7
33
|
include FlexMock::TestCase
|
8
34
|
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.
|
2
|
+
rubygems_version: 0.9.0.1
|
3
3
|
specification_version: 1
|
4
4
|
name: flexmock
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2006-
|
6
|
+
version: 0.4.0
|
7
|
+
date: 2006-09-04 00:00:00 -04:00
|
8
8
|
summary: Simple and Flexible Mock Objects for Testing
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -40,9 +40,11 @@ files:
|
|
40
40
|
- test/test_record_mode.rb
|
41
41
|
- test/test_samples.rb
|
42
42
|
- test/test_should_receive.rb
|
43
|
+
- test/test_stubbing.rb
|
43
44
|
- test/test_tu_integration.rb
|
44
45
|
- flexmock.blurb
|
45
46
|
- install.rb
|
47
|
+
- doc/releases/flexmock-0.4.0.rdoc
|
46
48
|
test_files: []
|
47
49
|
|
48
50
|
rdoc_options:
|
@@ -52,8 +54,9 @@ rdoc_options:
|
|
52
54
|
- README
|
53
55
|
- --line-numbers
|
54
56
|
extra_rdoc_files:
|
55
|
-
- CHANGELOG
|
56
57
|
- README
|
58
|
+
- CHANGELOG
|
59
|
+
- doc/releases/flexmock-0.4.0.rdoc
|
57
60
|
executables: []
|
58
61
|
|
59
62
|
extensions: []
|