flexmock 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/CHANGELOG +10 -1
  2. data/README +390 -209
  3. data/Rakefile +31 -10
  4. data/doc/GoogleExample.rdoc +275 -0
  5. data/doc/releases/flexmock-0.6.0.rdoc +136 -0
  6. data/lib/flexmock.rb +3 -1160
  7. data/lib/flexmock/argument_matchers.rb +57 -0
  8. data/lib/flexmock/argument_types.rb +42 -0
  9. data/lib/flexmock/base.rb +22 -0
  10. data/lib/flexmock/composite.rb +10 -0
  11. data/lib/flexmock/core.rb +206 -0
  12. data/lib/flexmock/core_class_methods.rb +92 -0
  13. data/lib/flexmock/default_framework_adapter.rb +31 -0
  14. data/lib/flexmock/expectation.rb +334 -0
  15. data/lib/flexmock/expectation_director.rb +59 -0
  16. data/lib/flexmock/mock_container.rb +159 -0
  17. data/lib/flexmock/noop.rb +13 -0
  18. data/lib/flexmock/partial_mock.rb +226 -0
  19. data/lib/flexmock/recorder.rb +71 -0
  20. data/lib/flexmock/rspec.rb +34 -0
  21. data/lib/flexmock/test_unit.rb +32 -0
  22. data/lib/flexmock/test_unit_integration.rb +53 -0
  23. data/lib/flexmock/validators.rb +77 -0
  24. data/test/rspec_integration/integration_spec.rb +36 -0
  25. data/test/test_container_methods.rb +119 -0
  26. data/test/test_default_framework_adapter.rb +39 -0
  27. data/test/test_example.rb +1 -1
  28. data/test/test_extended_should_receive.rb +63 -0
  29. data/test/test_mock.rb +1 -1
  30. data/test/test_naming.rb +1 -1
  31. data/test/{test_any_instance.rb → test_new_instances.rb} +15 -8
  32. data/test/{test_stubbing.rb → test_partial_mock.rb} +44 -44
  33. data/test/test_record_mode.rb +1 -1
  34. data/test/test_samples.rb +6 -8
  35. data/test/test_should_receive.rb +7 -3
  36. data/test/test_tu_integration.rb +1 -1
  37. data/test/test_unit_integration/test_auto_test_unit.rb +34 -0
  38. metadata +30 -5
  39. data/test/test_class_interception.rb +0 -140
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  #---
4
- # Copyright 2003, 2004, 2005, 2006 by Jim Weirich (jim@weirichhouse.org).
4
+ # Copyright 2003, 2004, 2005, 2006, 2007 by Jim Weirich (jim@weirichhouse.org).
5
5
  # All rights reserved.
6
6
 
7
7
  # Permission is granted for use, copying, modification, distribution,
@@ -9,1162 +9,5 @@
9
9
  # above copyright notice is included.
10
10
  #+++
11
11
 
12
- require 'test/unit'
13
-
14
- ######################################################################
15
- # FlexMock is a flexible mock object suitable for using with Ruby's
16
- # Test::Unit unit test framework. FlexMock has a simple interface
17
- # that's easy to remember, and leaves the hard stuff to all those
18
- # other mock object implementations.
19
- #
20
- # Basic Usage:
21
- #
22
- # m = FlexMock.new("name")
23
- # m.mock_handle(:meth) { |args| assert_stuff }
24
- #
25
- # Simplified Usage:
26
- #
27
- # m = FlexMock.new("name")
28
- # m.should_receive(:upcase).with("stuff").
29
- # and_return("STUFF")
30
- # m.should_receive(:downcase).with(String).
31
- # and_return { |s| s.downcase }.once
32
- #
33
- # With Test::Unit Integration:
34
- #
35
- # class TestSomething < Test::Unit::TestCase
36
- # include FlexMock::TestCase
37
- #
38
- # def test_something
39
- # m = flexmock("name")
40
- # m.should_receive(:hi).and_return("Hello")
41
- # m.hi
42
- # end
43
- # end
44
- #
45
- # Note: When using Test::Unit integeration, don't forget to include
46
- # FlexMock::TestCase. Also, if you override +teardown+, make sure you
47
- # call +super+.
48
- #
49
- class FlexMock
50
- include Test::Unit::Assertions
51
-
52
- class BadInterceptionError < RuntimeError; end
53
-
54
- attr_reader :mock_name, :mock_groups
55
- attr_accessor :mock_current_order, :mock_container
56
-
57
- # Create a FlexMock object with the given name. The name is used in
58
- # error messages.
59
- def initialize(name="unknown")
60
- @mock_name = name
61
- @expectations = Hash.new
62
- @allocated_order = 0
63
- @mock_current_order = 0
64
- @mock_container = nil
65
- @mock_groups = {}
66
- @ignore_missing = false
67
- @verified = false
68
- end
69
-
70
- # Handle all messages denoted by +sym+ by calling the given block
71
- # and passing any parameters to the block. If we know exactly how
72
- # many calls are to be made to a particular method, we may check
73
- # that by passing in the number of expected calls as a second
74
- # paramter.
75
- def mock_handle(sym, expected_count=nil, &block)
76
- self.should_receive(sym).times(expected_count).returns(&block)
77
- end
78
-
79
- # Verify that each method that had an explicit expected count was
80
- # actually called that many times.
81
- def mock_verify
82
- return if @verified
83
- @verified = true
84
- mock_wrap do
85
- @expectations.each do |sym, handler|
86
- handler.mock_verify
87
- end
88
- end
89
- end
90
-
91
- # Teardown and infrastructure setup for this mock.
92
- def mock_teardown
93
- end
94
-
95
- # Allocation a new order number from the mock.
96
- def mock_allocate_order
97
- @auto_allocate = true
98
- @allocated_order += 1
99
- end
100
-
101
- # Ignore all undefined (missing) method calls.
102
- def should_ignore_missing
103
- @ignore_missing = true
104
- end
105
- alias mock_ignore_missing should_ignore_missing
106
-
107
- # Handle missing methods by attempting to look up a handler.
108
- def method_missing(sym, *args, &block)
109
- mock_wrap do
110
- if handler = @expectations[sym]
111
- args << block if block_given?
112
- handler.call(*args)
113
- else
114
- super(sym, *args, &block) unless @ignore_missing
115
- end
116
- end
117
- end
118
-
119
- # Save the original definition of respond_to? for use a bit later.
120
- alias mock_respond_to? respond_to?
121
-
122
- # Override the built-in respond_to? to include the mocked methods.
123
- def respond_to?(sym)
124
- super || (@expectations[sym] ? true : @ignore_missing)
125
- end
126
-
127
- # Override the built-in +method+ to include the mocked methods.
128
- def method(sym)
129
- @expectations[sym] || super
130
- rescue NameError => ex
131
- if @ignore_missing
132
- proc { }
133
- else
134
- raise ex
135
- end
136
- end
137
-
138
- # Declare that the mock object should receive a message with the
139
- # given name. An expectation object for the method name is returned
140
- # as the result of this method. Further expectation constraints can
141
- # be added by chaining to the result.
142
- #
143
- # See Expectation for a list of declarators that can be used.
144
- def should_receive(sym)
145
- @expectations[sym] ||= ExpectationDirector.new(sym)
146
- result = Expectation.new(self, sym)
147
- @expectations[sym] << result
148
- override_existing_method(sym) if mock_respond_to?(sym)
149
- result
150
- end
151
-
152
- # Override the existing definition of method +sym+ in the mock.
153
- # Most methods depend on the method_missing trick to be invoked.
154
- # However, if the method already exists, it will not call
155
- # method_missing. This method defines a singleton method on the
156
- # mock to explicitly invoke the method_missing logic.
157
- def override_existing_method(sym)
158
- sclass.class_eval <<-EOS
159
- def #{sym}(*args, &block)
160
- method_missing(:#{sym}, *args, &block)
161
- end
162
- EOS
163
- end
164
- private :override_existing_method
165
-
166
- # Return the singleton class of the mock object.
167
- def sclass
168
- class << self; self; end
169
- end
170
- private :sclass
171
-
172
- # Declare that the mock object should expect methods by providing a
173
- # recorder for the methods and having the user invoke the expected
174
- # methods in a block. Further expectations may be applied the
175
- # result of the recording call.
176
- #
177
- # Example Usage:
178
- #
179
- # mock.should_expect do |record|
180
- # record.add(Integer, 4) { |a, b|
181
- # a + b
182
- # }.at_least.once
183
- #
184
- def should_expect
185
- yield Recorder.new(self)
186
- end
187
-
188
- # Return a factory object that returns this mock. This is useful in
189
- # Class Interception.
190
- def mock_factory
191
- Factory.new(self)
192
- end
193
-
194
- class << self
195
- include Test::Unit::Assertions
196
-
197
- # Class method to make sure that verify is called at the end of a
198
- # test. One mock object will be created for each name given to
199
- # the use method. The mocks will be passed to the block as
200
- # arguments. If no names are given, then a single anonymous mock
201
- # object will be created.
202
- #
203
- # At the end of the use block, each mock object will be verified
204
- # to make sure the proper number of calls have been made.
205
- #
206
- # Usage:
207
- #
208
- # FlexMock.use("name") do |mock| # Creates a mock named "name"
209
- # mock.should_receive(:meth).
210
- # returns(0).once
211
- # end # mock is verified here
212
- #
213
- # NOTE: If you include FlexMock::TestCase into your test case
214
- # file, you can create mocks that will be automatically verified in
215
- # the test teardown by using the +flexmock+ method.
216
- #
217
- def use(*names)
218
- names = ["unknown"] if names.empty?
219
- got_excecption = false
220
- mocks = names.collect { |n| new(n) }
221
- yield(*mocks)
222
- rescue Exception => ex
223
- got_exception = true
224
- raise
225
- ensure
226
- mocks.each do |mock|
227
- mock.mock_verify unless got_exception
228
- end
229
- end
230
-
231
- # Class method to format a method name and argument list as a nice
232
- # looking string.
233
- def format_args(sym, args)
234
- if args
235
- "#{sym}(#{args.collect { |a| a.inspect }.join(', ')})"
236
- else
237
- "#{sym}(*args)"
238
- end
239
- end
240
-
241
- # Check will assert the block returns true. If it doesn't, an
242
- # assertion failure is triggered with the given message.
243
- def check(msg, &block)
244
- assert_block(msg, &block)
245
- end
246
- end
247
-
248
- private
249
-
250
- # Wrap a block of code so the any assertion errors are wrapped so
251
- # that the mock name is added to the error message .
252
- def mock_wrap(&block)
253
- yield
254
- rescue Test::Unit::AssertionFailedError => ex
255
- raise Test::Unit::AssertionFailedError,
256
- "in mock '#{@mock_name}': #{ex.message}",
257
- ex.backtrace
258
- end
259
-
260
- ####################################################################
261
- # A Factory object is returned from a mock_factory method call. The
262
- # factory merely returns the manufactured object it is initialized
263
- # with. The factory is handy to use with class interception,
264
- # allowing the intercepted class to return the mock object.
265
- #
266
- # If the user needs more control over the mock factory, they are
267
- # free to create their own.
268
- #
269
- # Typical Usage:
270
- # intercept(Bar).in(Foo).with(a_mock.mack_factory)
271
- #
272
- class Factory
273
- def initialize(manufactured_object)
274
- @obj = manufactured_object
275
- end
276
- def new(*args, &block)
277
- @obj
278
- end
279
- end
280
-
281
- ####################################################################
282
- # The expectation director is responsible for routing calls to the
283
- # correct expectations for a given argument list.
284
- #
285
- class ExpectationDirector
286
-
287
- # Create an ExpectationDirector for a mock object.
288
- def initialize(sym)
289
- @sym = sym
290
- @expectations = []
291
- @expected_order = nil
292
- end
293
-
294
- # Invoke the expectations for a given set of arguments.
295
- #
296
- # First, look for an expectation that matches the arguements and
297
- # is eligible to be called. Failing that, look for a expectation
298
- # that matches the arguments (at this point it will be ineligible,
299
- # but at least we will get a good failure message). Finally,
300
- # check for expectations that don't have any argument matching
301
- # criteria.
302
- def call(*args)
303
- exp = @expectations.find { |e| e.match_args(args) && e.eligible? } ||
304
- @expectations.find { |e| e.match_args(args) }
305
- FlexMock.check("no matching handler found for " +
306
- FlexMock.format_args(@sym, args)) { ! exp.nil? }
307
- exp.verify_call(*args)
308
- end
309
-
310
- # Append an expectation to this director.
311
- def <<(expectation)
312
- @expectations << expectation
313
- end
314
-
315
- # Do the post test verification for this directory. Check all the
316
- # expectations.
317
- def mock_verify
318
- @expectations.each do |exp|
319
- exp.mock_verify
320
- end
321
- end
322
- end
323
-
324
- ####################################################################
325
- # Match any object
326
- class AnyMatcher
327
- def ===(target)
328
- true
329
- end
330
- def inspect
331
- "ANY"
332
- end
333
- end
334
-
335
- ####################################################################
336
- # Match only things that are equal.
337
- class EqualMatcher
338
- def initialize(obj)
339
- @obj = obj
340
- end
341
- def ===(target)
342
- @obj == target
343
- end
344
- def inspect
345
- "==(#{@obj.inspect})"
346
- end
347
- end
348
-
349
- ANY = AnyMatcher.new
350
-
351
- ####################################################################
352
- # Match only things where the block evaluates to true.
353
- class ProcMatcher
354
- def initialize(&block)
355
- @block = block
356
- end
357
- def ===(target)
358
- @block.call(target)
359
- end
360
- def inspect
361
- "on{...}"
362
- end
363
- end
364
-
365
- ####################################################################
366
- # Include this module in your test class if you wish to use the +eq+
367
- # and +any+ argument matching methods without a prefix. (Otherwise
368
- # use <tt>FlexMock.any</tt> and <tt>FlexMock.eq(obj)</tt>.
369
- #
370
- module ArgumentTypes
371
- # Return an argument matcher that matches any argument.
372
- def any
373
- ANY
374
- end
375
-
376
- # Return an argument matcher that only matches things equal to
377
- # (==) the given object.
378
- def eq(obj)
379
- EqualMatcher.new(obj)
380
- end
381
-
382
- # Return an argument matcher that matches any object, that when
383
- # passed to the supplied block, will cause the block to return
384
- # true.
385
- def on(&block)
386
- ProcMatcher.new(&block)
387
- end
388
- end
389
- extend ArgumentTypes
390
-
391
- ####################################################################
392
- # Base class for all the count validators.
393
- #
394
- class CountValidator
395
- include Test::Unit::Assertions
396
- def initialize(expectation, limit)
397
- @exp = expectation
398
- @limit = limit
399
- end
400
-
401
- # If the expectation has been called +n+ times, is it still
402
- # eligible to be called again? The default answer compares n to
403
- # the established limit.
404
- def eligible?(n)
405
- n < @limit
406
- end
407
- end
408
-
409
- ####################################################################
410
- # Validator for exact call counts.
411
- #
412
- class ExactCountValidator < CountValidator
413
- # Validate that the method expectation was called exactly +n+
414
- # times.
415
- def validate(n)
416
- assert_equal @limit, n,
417
- "method '#{@exp}' called incorrect number of times"
418
- end
419
- end
420
-
421
- ####################################################################
422
- # Validator for call counts greater than or equal to a limit.
423
- #
424
- class AtLeastCountValidator < CountValidator
425
- # Validate the method expectation was called no more than +n+
426
- # times.
427
- def validate(n)
428
- assert n >= @limit,
429
- "Method '#{@exp}' should be called at least #{@limit} times,\n" +
430
- "only called #{n} times"
431
- end
432
-
433
- # If the expectation has been called +n+ times, is it still
434
- # eligible to be called again? Since this validator only
435
- # establishes a lower limit, not an upper limit, then the answer
436
- # is always true.
437
- def eligible?(n)
438
- true
439
- end
440
- end
441
-
442
- ####################################################################
443
- # Validator for call counts less than or equal to a limit.
444
- #
445
- class AtMostCountValidator < CountValidator
446
- # Validate the method expectation was called at least +n+ times.
447
- def validate(n)
448
- assert n <= @limit,
449
- "Method '#{@exp}' should be called at most #{@limit} times,\n" +
450
- "only called #{n} times"
451
- end
452
- end
453
-
454
- ####################################################################
455
- # An Expectation is returned from each +should_receive+ message sent
456
- # to mock object. Each expectation records how a message matching
457
- # the message name (argument to +should_receive+) and the argument
458
- # list (given by +with+) should behave. Mock expectations can be
459
- # recorded by chaining the declaration methods defined in this
460
- # class.
461
- #
462
- # For example:
463
- #
464
- # mock.should_receive(:meth).with(args).and_returns(result)
465
- #
466
- class Expectation
467
- include Test::Unit::Assertions
468
-
469
- attr_reader :expected_args, :mock, :order_number
470
-
471
- # Create an expectation for a method named +sym+.
472
- def initialize(mock, sym)
473
- @mock = mock
474
- @sym = sym
475
- @expected_args = nil
476
- @count_validators = []
477
- @count_validator_class = ExactCountValidator
478
- @actual_count = 0
479
- @return_value = nil
480
- @return_block = lambda { @return_value }
481
- @order_number = nil
482
- end
483
-
484
- def to_s
485
- FlexMock.format_args(@sym, @expected_args)
486
- end
487
-
488
- # Verify the current call with the given arguments matches the
489
- # expectations recorded in this object.
490
- def verify_call(*args)
491
- validate_order
492
- @actual_count += 1
493
- @return_block.call(*args)
494
- end
495
-
496
- # Is this expectation eligible to be called again? It is eligible
497
- # only if all of its count validators agree that it is eligible.
498
- def eligible?
499
- @count_validators.all? { |v| v.eligible?(@actual_count) }
500
- end
501
-
502
- # Validate that the order
503
- def validate_order
504
- return if @order_number.nil?
505
- FlexMock.check("method #{to_s} called out of order " +
506
- "(expected order #{@order_number}, was #{@mock.mock_current_order})") {
507
- @order_number >= @mock.mock_current_order
508
- }
509
- @mock.mock_current_order = @order_number
510
- end
511
- private :validate_order
512
-
513
- # Validate the correct number of calls have been made. Called by
514
- # the teardown process.
515
- def mock_verify
516
- @count_validators.each do |v|
517
- v.validate(@actual_count)
518
- end
519
- end
520
-
521
- # Does the argument list match this expectation's argument
522
- # specification.
523
- def match_args(args)
524
- # TODO: Rethink this:
525
- # return false if @expected_args.nil?
526
- return true if @expected_args.nil?
527
- return false if args.size != @expected_args.size
528
- (0...args.size).all? { |i| match_arg(@expected_args[i], args[i]) }
529
- end
530
-
531
- # Does the expected argument match the corresponding actual value.
532
- def match_arg(expected, actual)
533
- expected === actual ||
534
- expected == actual ||
535
- ( Regexp === expected && expected === actual.to_s )
536
- end
537
-
538
- # Declare that the method should expect the given argument list.
539
- def with(*args)
540
- @expected_args = args
541
- self
542
- end
543
-
544
- # Declare that the method should be called with no arguments.
545
- def with_no_args
546
- with
547
- end
548
-
549
- # Declare that the method can be called with any number of
550
- # arguments of any type.
551
- def with_any_args
552
- @expected_args = nil
553
- self
554
- end
555
-
556
- # Declare that the method returns a particular value (when the
557
- # argument list is matched).
558
- #
559
- # * If a single value is given, it will be returned for all matching
560
- # calls.
561
- # * If multiple values are given, each value will be returned in turn for
562
- # each successive call. If the number of matching calls is greater
563
- # than the number of values, the last value will be returned for
564
- # the extra matching calls.
565
- # * If a block is given, it is evaluated on each call and its
566
- # value is returned.
567
- #
568
- # For example:
569
- #
570
- # mock.should_receive(:f).returns(12) # returns 12
571
- #
572
- # mock.should_receive(:f).with(String). # returns an
573
- # returns { |str| str.upcase } # upcased string
574
- #
575
- # +and_return+ is an alias for +returns+.
576
- #
577
- def returns(*args, &block)
578
- @return_block =
579
- if block_given?
580
- block
581
- else
582
- lambda { args.size == 1 ? args.first : args.shift }
583
- end
584
- self
585
- end
586
- alias :and_return :returns # :nodoc:
587
-
588
- # Declare that the method may be called any number of times.
589
- def zero_or_more_times
590
- at_least.never
591
- end
592
-
593
- # Declare that the method is called +limit+ times with the
594
- # declared argument list. This may be modified by the +at_least+
595
- # and +at_most+ declarators.
596
- def times(limit)
597
- @count_validators << @count_validator_class.new(self, limit) unless limit.nil?
598
- @count_validator_class = ExactCountValidator
599
- self
600
- end
601
-
602
- # Declare that the method is never expected to be called with the
603
- # given argument list. This may be modified by the +at_least+ and
604
- # +at_most+ declarators.
605
- def never
606
- times(0)
607
- end
608
-
609
- # Declare that the method is expected to be called exactly once
610
- # with the given argument list. This may be modified by the
611
- # +at_least+ and +at_most+ declarators.
612
- def once
613
- times(1)
614
- end
615
-
616
- # Declare that the method is expected to be called exactly twice
617
- # with the given argument list. This may be modified by the
618
- # +at_least+ and +at_most+ declarators.
619
- def twice
620
- times(2)
621
- end
622
-
623
- # Modifies the next call count declarator (+times+, +never+,
624
- # +once+ or +twice+) so that the declarator means the method is
625
- # called at least that many times.
626
- #
627
- # E.g. method f must be called at least twice:
628
- #
629
- # mock.should_receive(:f).at_least.twice
630
- #
631
- def at_least
632
- @count_validator_class = AtLeastCountValidator
633
- self
634
- end
635
-
636
- # Modifies the next call count declarator (+times+, +never+,
637
- # +once+ or +twice+) so that the declarator means the method is
638
- # called at most that many times.
639
- #
640
- # E.g. method f must be called no more than twice
641
- #
642
- # mock.should_receive(:f).at_most.twice
643
- #
644
- def at_most
645
- @count_validator_class = AtMostCountValidator
646
- self
647
- end
648
-
649
- # Declare that the given method must be called in order. All
650
- # ordered method calls must be received in the order specified by
651
- # the ordering of the +should_receive+ messages. Receiving a
652
- # methods out of the specified order will cause a test failure.
653
- #
654
- # If the user needs more fine control over ordering
655
- # (e.g. specifying that a group of messages may be received in any
656
- # order as long as they all come after another group of messages),
657
- # a _group_ _name_ may be specified in the +ordered+ calls. All
658
- # messages within the same group may be received in any order.
659
- #
660
- # For example, in the following, messages +flip+ and +flop+ may be
661
- # received in any order (because they are in the same group), but
662
- # must occur strictly after +start+ but before +end+. The message
663
- # +any_time+ may be received at any time because it is not
664
- # ordered.
665
- #
666
- # m = FlexMock.new
667
- # m.should_receive(:any_time)
668
- # m.should_receive(:start).ordered
669
- # m.should_receive(:flip).ordered(:flip_flop_group)
670
- # m.should_receive(:flop).ordered(:flip_flop_group)
671
- # m.should_receive(:end).ordered
672
- #
673
- def ordered(group_name=nil)
674
- if group_name.nil?
675
- @order_number = @mock.mock_allocate_order
676
- elsif (num = @mock.mock_groups[group_name])
677
- @order_number = num
678
- else
679
- @order_number = @mock.mock_allocate_order
680
- @mock.mock_groups[group_name] = @order_number
681
- end
682
- self
683
- end
684
- end
685
-
686
- ####################################################################
687
- # Translate arbitrary method calls into expectations on the given
688
- # mock object.
689
- #
690
- class Recorder
691
- include FlexMock::ArgumentTypes
692
-
693
- # Create a method recorder for the mock +mock+.
694
- def initialize(mock)
695
- @mock = mock
696
- @strict = false
697
- end
698
-
699
- # Place the record in strict mode. While recording expectations
700
- # in strict mode, the following will be true.
701
- #
702
- # * All expectations will be expected in the order they were
703
- # recorded.
704
- # * All expectations will be expected once.
705
- # * All arguments will be placed in exact match mode,
706
- # including regular expressions and class objects.
707
- #
708
- # Strict mode is usually used when giving the recorder to a known
709
- # good algorithm. Strict mode captures the exact sequence of
710
- # calls and validate that the code under test performs the exact
711
- # same sequence of calls.
712
- #
713
- # The recorder may exit strict mode via a
714
- # <tt>should_be_strict(false)</tt> call. Non-strict expectations
715
- # may be recorded at that point, or even explicit expectations
716
- # (using +should_receieve+) can be specified.
717
- #
718
- def should_be_strict(is_strict=true)
719
- @strict = is_strict
720
- end
721
-
722
- # Is the recorder in strict mode?
723
- def strict?
724
- @strict
725
- end
726
-
727
- # Record an expectation for receiving the method +sym+ with the
728
- # given arguments.
729
- def method_missing(sym, *args, &block)
730
- expectation = @mock.should_receive(sym).and_return(&block)
731
- if strict?
732
- args = args.collect { |arg| eq(arg) }
733
- expectation.with(*args).ordered.once
734
- else
735
- expectation.with(*args)
736
- end
737
- expectation
738
- end
739
- end
740
-
741
- ####################################################################
742
- # Test::Unit::TestCase Integration.
743
- #
744
- # Include this module in any TestCase class in a Test::Unit test
745
- # suite to get integration with FlexMock. When this module is
746
- # included, mocks may be created with a simple call to the
747
- # +flexmock+ method. Mocks created with via the method call will
748
- # automatically be verified in the teardown of the test case.
749
- #
750
- # <b>Note:</b> If you define a +teardown+ method in the test case,
751
- # <em>dont' forget to invoke the +super+ method!</em> Failure to
752
- # invoke super will cause all mocks to not be verified.
753
- #
754
- module TestCase
755
- include ArgumentTypes
756
-
757
- # Teardown the test case, verifying any mocks that might have been
758
- # defined in this test case.
759
- def teardown
760
- super
761
- flexmock_teardown
762
- end
763
-
764
- # Do the flexmock specific teardown stuff.
765
- def flexmock_teardown
766
- @flexmock_created_mocks ||= []
767
- if passed?
768
- @flexmock_created_mocks.each do |m|
769
- m.mock_verify
770
- end
771
- end
772
- ensure
773
- @flexmock_created_mocks.each do |m|
774
- m.mock_teardown
775
- end
776
- @flexmock_created_mocks = []
777
- @flexmock_interceptors ||= []
778
- @flexmock_interceptors.each do |i|
779
- i.restore
780
- end
781
- end
782
-
783
- # Create a FlexMock object with the given name. Mocks created
784
- # with this method will be automatically verify during teardown
785
- # (assuming the the flexmock teardown isn't overridden).
786
- #
787
- # If a block is given, then the mock object is passed to the block and
788
- # may be configured within the block.
789
- def flexmock(name="unknown")
790
- mock = FlexMock.new(name)
791
- yield(mock) if block_given?
792
- flexmock_remember(mock)
793
- mock
794
- end
795
-
796
- # Stub the given object by overriding the behavior of individual
797
- # methods. The stub object returned will respond to the
798
- # +should_receive+ method, just like normal stubs.
799
- #
800
- # If a block is given, then the stub object is passed to the block
801
- # and may be configured within the block.
802
- #
803
- # Example: Stub out DBI to return a fake db connection.
804
- #
805
- # flexstub(DBI) do |s|
806
- # s.should_receive(:connect).and_return {
807
- # flexmock("db connection") do |m|
808
- # m.should_receive(:select_all).and_return(...)
809
- # end
810
- # }
811
- # end
812
- #
813
- def flexstub(obj, name=nil)
814
- name ||= "flexstub(#{obj.class.to_s})"
815
- obj.instance_eval {
816
- @flexmock_proxy ||= StubProxy.new(obj, FlexMock.new(name))
817
- }
818
- proxy = obj.instance_variable_get("@flexmock_proxy")
819
- yield(proxy) if block_given?
820
- flexmock_remember(proxy)
821
- end
822
-
823
- # Intercept the named class in the target class for the duration
824
- # of the test. Class interception is very simple-minded and has a
825
- # number of restrictions. First, the intercepted class must be
826
- # reference in the tested class via a simple constant name
827
- # (e.g. no scoped names using "::") that is not directly defined
828
- # in the class itself. After the test, a proxy class constant
829
- # will be left behind that will forward all calls to the original
830
- # class.
831
- #
832
- # <b>Warning:</b> <em>Class Interception is deprecated. Use
833
- # flexstub instead.</em>
834
- #
835
- # Usage:
836
- # intercept(SomeClass).in(ClassBeingTested).with(MockClass)
837
- # intercept(SomeClass).with(MockClass).in(ClassBeingTested)
838
- #
839
- def intercept(intercepted_class)
840
- result = Interception.new(intercepted_class)
841
- @flexmock_interceptors ||= []
842
- @flexmock_interceptors << result
843
- result
844
- end
845
-
846
- private
847
-
848
- def flexmock_remember(mocking_object)
849
- @flexmock_created_mocks ||= []
850
- @flexmock_created_mocks << mocking_object
851
- mocking_object.mock_container = self
852
- mocking_object
853
- end
854
- end
855
-
856
- ####################################################################
857
- # A Class Interception defines a constant in the target class to be
858
- # a proxy that points to a replacement class for the duration of a
859
- # test. When an interception is restored, the proxy will point to
860
- # the original intercepted class.
861
- #
862
- class Interception
863
- # Create an interception object with the class to intercepted.
864
- def initialize(intercepted_class)
865
- @intercepted = nil
866
- @target = nil
867
- @replacement = nil
868
- @proxy = nil
869
- intercept(intercepted_class)
870
- update
871
- end
872
-
873
- # Intercept this class in the class to be tested.
874
- def intercept(intercepted_class)
875
- self.class.show_intercept_warning
876
- @intercepted = intercepted_class
877
- update
878
- self
879
- end
880
-
881
- # Define the class number test that will receive the
882
- # interceptioned definition.
883
- def in(target_class)
884
- @target = target_class
885
- update
886
- self
887
- end
888
-
889
- # Define the replacement class. This is normally a proxy or a
890
- # stub.
891
- def with(replacement_class)
892
- @replacement = replacement_class
893
- update
894
- self
895
- end
896
-
897
- # Restore the original class. The proxy remains in place however.
898
- def restore
899
- @proxy.proxied_class = @restore_class if @proxy
900
- end
901
-
902
- private
903
-
904
- # Update the interception if the definition is complete.
905
- def update
906
- if complete?
907
- do_interception
908
- end
909
- end
910
-
911
- # Is the interception definition complete. In other words, are
912
- # all three actors defined?
913
- def complete?
914
- @intercepted && @target && @replacement
915
- end
916
-
917
- # Implement interception on the classes defined.
918
- def do_interception
919
- @target_class = coerce_class(@target, "target")
920
- @replacement_class = coerce_class(@replacement, "replacement")
921
- case @intercepted
922
- when String, Symbol
923
- @intercepted_name = @intercepted.to_s
924
- when Class
925
- @intercepted_name = @intercepted.name
926
- end
927
- @intercepted_class = coerce_class(@intercepted, "intercepted")
928
- current_class = @target_class.const_get(@intercepted_name)
929
- if ClassProxy === current_class
930
- @proxy = current_class
931
- @restore_class = @proxy.proxied_class
932
- @proxy.proxied_class = @replacement_class
933
- else
934
- @proxy = ClassProxy.new(@replacement_class)
935
- @restore_class = current_class
936
- @target_class.const_set(@intercepted_name, @proxy)
937
- end
938
- end
939
-
940
- # Coerce a class object, string to symbol to be the class object.
941
- def coerce_class(klass, where)
942
- case klass
943
- when String, Symbol
944
- lookup_const(klass.to_s, where)
945
- else
946
- klass
947
- end
948
- end
949
-
950
- def lookup_const(name, where, target=Object)
951
- begin
952
- target.const_get(name)
953
- rescue NameError
954
- raise BadInterceptionError, "in #{where} class #{name}"
955
- end
956
- end
957
-
958
- @shown_warning = false
959
-
960
- class << self
961
- def show_intercept_warning
962
- unless @shown_warning
963
- @shown_warning = true
964
- $stderr.puts "FlexMock Class Interception is deprecated and will be removed in future versions."
965
- end
966
- end
967
- end
968
- end
969
-
970
- ####################################################################
971
- # Class Proxy for class interception. Forward all method calls to
972
- # whatever is the proxied_class.
973
- #
974
- class ClassProxy
975
- attr_accessor :proxied_class
976
- def initialize(default_class)
977
- @proxied_class = default_class
978
- end
979
- def method_missing(sym, *args, &block)
980
- @proxied_class.__send__(sym, *args, &block)
981
- end
982
- end
983
-
984
- ####################################################################
985
- # StubProxy is used to mate the mock framework to an existing
986
- # object. The object is "enhanced" with a reference to a mock
987
- # object (stored in <tt>@flexmock_mock</tt>). When the
988
- # +should_receive+ method is sent to the proxy, it overrides the
989
- # existing object's method by creating singleton method that
990
- # forwards to the mock. When testing is complete, StubProxy
991
- # will erase the mocking infrastructure from the object being
992
- # stubbed (e.g. remove instance variables and mock singleton
993
- # methods).
994
- #
995
- class StubProxy
996
- attr_reader :mock
997
-
998
- # Initialize a StubProxy object.
999
- def initialize(obj, mock)
1000
- @obj = obj
1001
- @mock = mock
1002
- @method_definitions = {}
1003
- @methods_proxied = []
1004
- end
1005
-
1006
- # Stub out the given method in the existing object and then let the
1007
- # mock object handle should_receive.
1008
- def should_receive(method_name)
1009
- method_name = method_name.to_sym
1010
- unless @methods_proxied.include?(method_name)
1011
- hide_existing_method(method_name)
1012
- @methods_proxied << method_name
1013
- end
1014
- @mock.should_receive(method_name)
1015
- end
1016
-
1017
- # new_instances is a short cut method for overriding the behavior of any
1018
- # new instances created via a stubbed class object.
1019
- #
1020
- # By default, new_instances will stub the behaviour of the :new and
1021
- # :allocate methods. If you wish to stub a different set of class
1022
- # methods, just pass a list of symbols to as arguments.
1023
- #
1024
- # For example, to stub only objects created by :make (and not :new
1025
- # or :allocate), use:
1026
- #
1027
- # flexstub(ClassName).new_instances(:make) do |obj|
1028
- # obj.should_receive(...)
1029
- # end
1030
- #
1031
- def new_instances(*allocators, &block)
1032
- fail ArgumentError, "new_instances requires a Class to stub" unless Class === @obj
1033
- fail ArgumentError, "new_instances requires a block" unless block_given?
1034
- allocators = [:new, :allocate] if allocators.empty?
1035
- allocators.each do |m|
1036
- self.should_receive(m).and_return { |*args|
1037
- new_obj = invoke_original(m, args)
1038
- mock = mock_container.flexstub(new_obj)
1039
- block.call(mock)
1040
- new_obj
1041
- }
1042
- end
1043
- nil
1044
- end
1045
-
1046
- # any_instance is present for backwards compatibility with version 0.5.0.
1047
- # @deprecated
1048
- def any_instance(&block)
1049
- $stderr.puts "any_instance is deprecated, use new_instances instead."
1050
- new_instances(&block)
1051
- end
1052
-
1053
- # Invoke the original definition of method on the object supported by
1054
- # the stub.
1055
- def invoke_original(method, args)
1056
- method_proc = @method_definitions[method]
1057
- method_proc.call(*args)
1058
- end
1059
- private :invoke_original
1060
-
1061
- # Verify that the mock has been properly called. After verification,
1062
- # detach the mocking infrastructure from the existing object.
1063
- def mock_verify
1064
- @mock.mock_verify
1065
- end
1066
-
1067
- # Remove all traces of the mocking framework from the existing object.
1068
- def mock_teardown
1069
- if ! detached?
1070
- @methods_proxied.each do |method_name|
1071
- remove_current_method(method_name)
1072
- restore_original_definition(method_name)
1073
- end
1074
- @obj.instance_variable_set("@flexmock_proxy", nil)
1075
- @obj = nil
1076
- end
1077
- end
1078
-
1079
- # Return the container for this mocking object. Returns nil if the
1080
- # mock is not in a container. Mock containers make sure that mock objects
1081
- # inside the container are torn down at the end of a test
1082
- def mock_container
1083
- @mock.mock_container
1084
- end
1085
-
1086
- # Set the container for this mock object.
1087
- def mock_container=(container)
1088
- @mock.mock_container = container
1089
- end
1090
-
1091
- private
1092
-
1093
- # The singleton class of the object.
1094
- def sclass
1095
- class << @obj; self; end
1096
- end
1097
-
1098
- # Is the current method a singleton method in the object we are
1099
- # mocking?
1100
- def singleton?(method_name)
1101
- @obj.methods(false).include?(method_name.to_s)
1102
- end
1103
-
1104
- # Hide the existing method definition with a singleton defintion
1105
- # that proxies to our mock object. If the current definition is a
1106
- # singleton, we need to record the definition and remove it before
1107
- # creating our own singleton method. If the current definition is
1108
- # not a singleton, all we need to do is override it with our own
1109
- # singleton.
1110
- def hide_existing_method(method_name)
1111
- if @obj.respond_to?(method_name)
1112
- new_alias = new_name(method_name)
1113
- unless @obj.respond_to?(new_alias)
1114
- sclass.class_eval do
1115
- alias_method(new_alias, method_name)
1116
- end
1117
- end
1118
- my_object = @obj
1119
- @method_definitions[method_name] = Proc.new { |*args|
1120
- block = nil
1121
- if Proc === args.last
1122
- block = args.last
1123
- args = args[0...-1]
1124
- end
1125
- my_object.send(new_alias, *args, &block)
1126
- }
1127
- end
1128
- remove_current_method(method_name) if singleton?(method_name)
1129
- define_proxy_method(method_name)
1130
- end
1131
-
1132
- # Define a proxy method that forwards to our mock object. The
1133
- # proxy method is defined as a singleton method on the object
1134
- # being mocked.
1135
- def define_proxy_method(method_name)
1136
- sclass.class_eval %{
1137
- def #{method_name}(*args, &block) @flexmock_proxy.mock.#{method_name}(*args, &block) end
1138
- }
1139
- end
1140
-
1141
- # Restore the original singleton defintion for method_name that
1142
- # was saved earlier.
1143
- def restore_original_definition(method_name)
1144
- method_def = @method_definitions[method_name]
1145
- if method_def
1146
- the_alias = new_name(method_name)
1147
- sclass.class_eval do
1148
- alias_method(method_name, the_alias)
1149
- end
1150
- end
1151
- end
1152
-
1153
- # Remove the current method if it is a singleton method of the
1154
- # object being mocked.
1155
- def remove_current_method(method_name)
1156
- sclass.class_eval { remove_method(method_name) }
1157
- end
1158
-
1159
- # Have we been detached from the existing object?
1160
- def detached?
1161
- @obj.nil?
1162
- end
1163
-
1164
- # Generate a name to be used to alias the original behavior.
1165
- def new_name(old_name)
1166
- "flexmock_original_behavior_for_#{old_name}"
1167
- end
1168
-
1169
- end
1170
- end
12
+ require 'flexmock/base'
13
+ require 'flexmock/test_unit_integration'