flexmock 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'