Skip to content
This repository
Browse code

Merge [5917] from trunk: unbundle flexmock.

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

0 comments on commit 8f84165

Please sign in to comment.
Something went wrong with that request. Please try again.