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 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 uniniitalized variables.
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 testing. The interface is
4
- simple, but still provides a good bit of flexibility.
3
+ FlexMock is a simple, but flexible, mock object library for Ruby unit
4
+ testing.
5
5
 
6
- Version :: 0.3.2
6
+ Version :: 0.4.0
7
7
 
8
8
  = Links
9
9
 
10
10
  <b>Documents</b> :: http://onestepback.org/software/flexmock
11
- <b>Download</b> :: Use RubyGems to download the flexmock gem from http://gems.rubyforge.org
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 test_tempurature_sampler
43
- readings = [10, 12, 14]
46
+ def test_temperature_sampler
44
47
  sensor = flexmock("temp")
45
- sensor.should_receive(:read_temperature).and_return { readings.shift }
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 +flexmock+ method will be
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>returns(<em>value</em>)</tt></b> -- Declares that the message
90
- will return the given value (<tt>returns(nil)</tt> is the default).
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 teh FlexMock.eq convenience
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
- FlexMock now support simple class interception. For the duration of a
221
- test, a mock class take the place of a named class inside the class to
222
- be tested.
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
- return_values = ["line 1\n", "line 2\n"]
344
- file.should_receive(:gets).with_no_args.and_return { return_values.shift }
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.3.2'
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('[A-Z][A-Z]*')
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
- handler.mock_verify
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
- args << block if block_given?
104
- handler.call(*args)
110
+ args << block if block_given?
111
+ handler.call(*args)
105
112
  else
106
- super(sym, *args, &block) unless @ignore_missing
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 moks that will be automatically verified in
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
- mock.mock_verify unless got_exception
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
- "#{sym}(#{args.collect { |a| a.inspect }.join(', ')})"
210
+ "#{sym}(#{args.collect { |a| a.inspect }.join(', ')})"
204
211
  else
205
- "#{sym}(*args)"
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 teh arguments (at this point it will be ineligible,
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
- @expectations.find { |e| e.expected_args.nil? }
280
+ @expectations.find { |e| e.expected_args.nil? }
274
281
  FlexMock.check("no matching handler found for " +
275
- FlexMock.format_args(@sym, args)) { ! exp.nil? }
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
- exp.mock_verify
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
- "method '#{@exp}' called incorrect number of times"
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
- "Method '#{@exp}' should be called at least #{@limit} times,\n" +
404
- "only called #{n} times"
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
- "Method '#{@exp}' should be called at most #{@limit} times,\n" +
424
- "only called #{n} times"
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
- "(expected order #{@order_number}, was #{@mock.mock_current_order})") {
481
- @order_number >= @mock.mock_current_order
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
- v.validate(@actual_count)
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
- expected == actual ||
507
- ( Regexp === expected && expected === actual.to_s )
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). If a block is given, it is evaluated
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
- def returns(value=nil, &block)
541
- @return_block = block_given? ? block : lambda { value }
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 # :nodoc:
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
- @order_number = @mock.mock_allocate_order
651
+ @order_number = @mock.mock_allocate_order
634
652
  elsif (num = @mock.mock_groups[group_name])
635
- @order_number = num
653
+ @order_number = num
636
654
  else
637
- @order_number = @mock.mock_allocate_order
638
- @mock.mock_groups[group_name] = @order_number
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
- result = FlexMock.new(name)
741
- @flexmock_created_mocks ||= []
742
- @flexmock_created_mocks << result
743
- result
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
- m.mock_handle(:blip, 0)
82
- m.blip
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
 
@@ -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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  #---
4
- # Copyright 2003 by Jim Weirich (jweirich@one.net).
4
+ # Copyright 2006 by Jim Weirich (jweirich@one.net).
5
5
  # All rights reserved.
6
6
 
7
7
  # Permission is granted for use, copying, modification, distribution,
@@ -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
@@ -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.8.99.1
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.3.2
7
- date: 2006-06-17 00:00:00 -04:00
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: []