flexmock 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|