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
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003, 2004, 2005, 2006, 2007 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
+ # No-op include file. Used as a kludge so that only the comments in the
13
+ # core.rb file are applied to the FlexMock class.
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003, 2004, 2005, 2006, 2007 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 'flexmock/noop'
13
+
14
+ class FlexMock
15
+
16
+ ####################################################################
17
+ # PartialMock is used to mate the mock framework to an existing
18
+ # object. The object is "enhanced" with a reference to a mock
19
+ # object (stored in <tt>@flexmock_mock</tt>). When the
20
+ # +should_receive+ method is sent to the proxy, it overrides the
21
+ # existing object's method by creating singleton method that
22
+ # forwards to the mock. When testing is complete, PartialMock
23
+ # will erase the mocking infrastructure from the object being
24
+ # mocked (e.g. remove instance variables and mock singleton
25
+ # methods).
26
+ #
27
+ class PartialMock
28
+ attr_reader :mock
29
+
30
+ # Initialize a PartialMock object.
31
+ def initialize(obj, mock)
32
+ @obj = obj
33
+ @mock = mock
34
+ @method_definitions = {}
35
+ @methods_proxied = []
36
+ end
37
+
38
+ # :call-seq:
39
+ # should_receive(:method_name)
40
+ # should_receive(:method1, method2, ...)
41
+ # should_receive(:meth1 => result1, :meth2 => result2, ...)
42
+ #
43
+ # Declare that the partial mock should receive a message with the given
44
+ # name.
45
+ #
46
+ # If more than one method name is given, then the mock object should
47
+ # expect to receive all the listed melthods. If a hash of method
48
+ # name/value pairs is given, then the each method will return the
49
+ # associated result. Any expectations applied to the result of
50
+ # +should_receive+ will be applied to all the methods defined in the
51
+ # argument list.
52
+ #
53
+ # An expectation object for the method name is returned as the result of
54
+ # this method. Further expectation constraints can be added by chaining
55
+ # to the result.
56
+ #
57
+ # See Expectation for a list of declarators that can be used.
58
+ def should_receive(*args)
59
+ FlexMock.should_receive(args) do |sym|
60
+ unless @methods_proxied.include?(sym)
61
+ hide_existing_method(sym)
62
+ @methods_proxied << sym
63
+ end
64
+ ex = @mock.should_receive(sym)
65
+ ex.mock = self
66
+ ex
67
+ end
68
+ end
69
+
70
+ # :call-seq:
71
+ # new_instances.should_receive(...)
72
+ # new_instances { |instance| instance.should_receive(...) }
73
+ #
74
+ # new_instances is a short cut method for overriding the behavior of any
75
+ # new instances created via a mocked class object.
76
+ #
77
+ # By default, new_instances will mock the behaviour of the :new and
78
+ # :allocate methods. If you wish to mock a different set of class
79
+ # methods, just pass a list of symbols to as arguments.
80
+ #
81
+ # For example, to stub only objects created by :make (and not :new
82
+ # or :allocate), use:
83
+ #
84
+ # flexmock(ClassName).new_instances(:make).should_receive(...)
85
+ #
86
+ def new_instances(*allocators, &block)
87
+ fail ArgumentError, "new_instances requires a Class to stub" unless Class === @obj
88
+ allocators = [:new, :allocate] if allocators.empty?
89
+ result = ExpectationRecorder.new
90
+ allocators.each do |m|
91
+ self.should_receive(m).and_return { |*args|
92
+ new_obj = invoke_original(m, args)
93
+ mock = mock_container.flexmock(new_obj)
94
+ block.call(mock) if block_given?
95
+ result.apply(mock)
96
+ new_obj
97
+ }
98
+ end
99
+ result
100
+ end
101
+
102
+ # any_instance is present for backwards compatibility with version 0.5.0.
103
+ # @deprecated
104
+ def any_instance(&block)
105
+ $stderr.puts "any_instance is deprecated, use new_instances instead."
106
+ new_instances(&block)
107
+ end
108
+
109
+ # Invoke the original definition of method on the object supported by
110
+ # the stub.
111
+ def invoke_original(method, args)
112
+ method_proc = @method_definitions[method]
113
+ method_proc.call(*args)
114
+ end
115
+ private :invoke_original
116
+
117
+ # Verify that the mock has been properly called. After verification,
118
+ # detach the mocking infrastructure from the existing object.
119
+ def mock_verify
120
+ @mock.mock_verify
121
+ end
122
+
123
+ # Remove all traces of the mocking framework from the existing object.
124
+ def mock_teardown
125
+ if ! detached?
126
+ @methods_proxied.each do |method_name|
127
+ remove_current_method(method_name)
128
+ restore_original_definition(method_name)
129
+ end
130
+ @obj.instance_variable_set("@flexmock_proxy", nil)
131
+ @obj = nil
132
+ end
133
+ end
134
+
135
+ # Return the container for this mocking object. Returns nil if the
136
+ # mock is not in a container. Mock containers make sure that mock objects
137
+ # inside the container are torn down at the end of a test
138
+ def mock_container
139
+ @mock.mock_container
140
+ end
141
+
142
+ # Set the container for this mock object.
143
+ def mock_container=(container)
144
+ @mock.mock_container = container
145
+ end
146
+
147
+ private
148
+
149
+ # The singleton class of the object.
150
+ def sclass
151
+ class << @obj; self; end
152
+ end
153
+
154
+ # Is the current method a singleton method in the object we are
155
+ # mocking?
156
+ def singleton?(method_name)
157
+ @obj.methods(false).include?(method_name.to_s)
158
+ end
159
+
160
+ # Hide the existing method definition with a singleton defintion
161
+ # that proxies to our mock object. If the current definition is a
162
+ # singleton, we need to record the definition and remove it before
163
+ # creating our own singleton method. If the current definition is
164
+ # not a singleton, all we need to do is override it with our own
165
+ # singleton.
166
+ def hide_existing_method(method_name)
167
+ if @obj.respond_to?(method_name)
168
+ new_alias = new_name(method_name)
169
+ unless @obj.respond_to?(new_alias)
170
+ sclass.class_eval do
171
+ alias_method(new_alias, method_name)
172
+ end
173
+ end
174
+ my_object = @obj
175
+ @method_definitions[method_name] = Proc.new { |*args|
176
+ block = nil
177
+ if Proc === args.last
178
+ block = args.last
179
+ args = args[0...-1]
180
+ end
181
+ my_object.send(new_alias, *args, &block)
182
+ }
183
+ end
184
+ remove_current_method(method_name) if singleton?(method_name)
185
+ define_proxy_method(method_name)
186
+ end
187
+
188
+ # Define a proxy method that forwards to our mock object. The
189
+ # proxy method is defined as a singleton method on the object
190
+ # being mocked.
191
+ def define_proxy_method(method_name)
192
+ sclass.class_eval %{
193
+ def #{method_name}(*args, &block) @flexmock_proxy.mock.#{method_name}(*args, &block) end
194
+ }
195
+ end
196
+
197
+ # Restore the original singleton defintion for method_name that
198
+ # was saved earlier.
199
+ def restore_original_definition(method_name)
200
+ method_def = @method_definitions[method_name]
201
+ if method_def
202
+ the_alias = new_name(method_name)
203
+ sclass.class_eval do
204
+ alias_method(method_name, the_alias)
205
+ end
206
+ end
207
+ end
208
+
209
+ # Remove the current method if it is a singleton method of the
210
+ # object being mocked.
211
+ def remove_current_method(method_name)
212
+ sclass.class_eval { remove_method(method_name) }
213
+ end
214
+
215
+ # Have we been detached from the existing object?
216
+ def detached?
217
+ @obj.nil?
218
+ end
219
+
220
+ # Generate a name to be used to alias the original behavior.
221
+ def new_name(old_name)
222
+ "flexmock_original_behavior_for_#{old_name}"
223
+ end
224
+
225
+ end
226
+ end
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003, 2004, 2005, 2006, 2007 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 'flexmock/argument_types'
13
+
14
+ class FlexMock
15
+
16
+ ####################################################################
17
+ # Translate arbitrary method calls into expectations on the given
18
+ # mock object.
19
+ #
20
+ class Recorder
21
+ include FlexMock::ArgumentTypes
22
+
23
+ # Create a method recorder for the mock +mock+.
24
+ def initialize(mock)
25
+ @mock = mock
26
+ @strict = false
27
+ end
28
+
29
+ # Place the record in strict mode. While recording expectations
30
+ # in strict mode, the following will be true.
31
+ #
32
+ # * All expectations will be expected in the order they were
33
+ # recorded.
34
+ # * All expectations will be expected once.
35
+ # * All arguments will be placed in exact match mode,
36
+ # including regular expressions and class objects.
37
+ #
38
+ # Strict mode is usually used when giving the recorder to a known
39
+ # good algorithm. Strict mode captures the exact sequence of
40
+ # calls and validate that the code under test performs the exact
41
+ # same sequence of calls.
42
+ #
43
+ # The recorder may exit strict mode via a
44
+ # <tt>should_be_strict(false)</tt> call. Non-strict expectations
45
+ # may be recorded at that point, or even explicit expectations
46
+ # (using +should_receieve+) can be specified.
47
+ #
48
+ def should_be_strict(is_strict=true)
49
+ @strict = is_strict
50
+ end
51
+
52
+ # Is the recorder in strict mode?
53
+ def strict?
54
+ @strict
55
+ end
56
+
57
+ # Record an expectation for receiving the method +sym+ with the
58
+ # given arguments.
59
+ def method_missing(sym, *args, &block)
60
+ expectation = @mock.should_receive(sym).and_return(&block)
61
+ if strict?
62
+ args = args.collect { |arg| eq(arg) }
63
+ expectation.with(*args).ordered.once
64
+ else
65
+ expectation.with(*args)
66
+ end
67
+ expectation
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003, 2004, 2005, 2006, 2007 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 'flexmock/base'
13
+
14
+ class FlexMock
15
+
16
+ class RSpecFrameworkAdapter
17
+ def assert_block(msg, &block)
18
+ Spec::Expectations.fail_with(msg) unless yield
19
+ end
20
+
21
+ def assert_equal(a, b, msg=nil)
22
+ message = msg || "Expected equal"
23
+ assert_block(message + "\n<#{a}> expected, but was\n<#{b}>") { a == b }
24
+ end
25
+
26
+ class AssertionFailedError < StandardError; end
27
+ def assertion_failed_error
28
+ Spec::Expectations::ExpectationNotMetError
29
+ end
30
+ end
31
+
32
+ @framework_adapter = RSpecFrameworkAdapter.new
33
+
34
+ end
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003, 2004, 2005, 2006, 2007 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 'flexmock/test_unit_integration'
13
+
14
+ module Test
15
+ module Unit
16
+ class TestCase
17
+ include FlexMock::ArgumentTypes
18
+ include FlexMock::MockContainer
19
+
20
+ # Alias the original teardown behavior for later use.
21
+ alias :flexmock_original_teardown :teardown
22
+
23
+ # Teardown the test case, verifying any mocks that might have been
24
+ # defined in this test case.
25
+ def teardown
26
+ flexmock_teardown
27
+ flexmock_original_teardown
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003, 2004, 2005, 2006, 2007 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
+ require 'flexmock/base'
14
+
15
+ class FlexMock
16
+
17
+ ####################################################################
18
+ # Test::Unit::TestCase Integration.
19
+ #
20
+ # Include this module in any TestCase class in a Test::Unit test
21
+ # suite to get integration with FlexMock. When this module is
22
+ # included, the mock container methods (e.g. flexmock(), flexstub())
23
+ # will be available.
24
+ #
25
+ # <b>Note:</b> If you define a +teardown+ method in the test case,
26
+ # <em>dont' forget to invoke the +super+ method!</em> Failure to
27
+ # invoke super will cause all mocks to not be verified.
28
+ #
29
+ module TestCase
30
+ include ArgumentTypes
31
+ include MockContainer
32
+
33
+ # Teardown the test case, verifying any mocks that might have been
34
+ # defined in this test case.
35
+ def teardown
36
+ super
37
+ flexmock_teardown
38
+ end
39
+
40
+ end
41
+
42
+ ####################################################################
43
+ # Adapter for adapting FlexMock to the Test::Unit framework.
44
+ #
45
+ class TestUnitFrameworkAdapter
46
+ include Test::Unit::Assertions
47
+ def assertion_failed_error
48
+ Test::Unit::AssertionFailedError
49
+ end
50
+ end
51
+
52
+ @framework_adapter = TestUnitFrameworkAdapter.new
53
+ end
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003, 2004, 2005, 2006, 2007 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 'flexmock/noop'
13
+
14
+ class FlexMock
15
+
16
+ ####################################################################
17
+ # Base class for all the count validators.
18
+ #
19
+ class CountValidator
20
+ def initialize(expectation, limit)
21
+ @exp = expectation
22
+ @limit = limit
23
+ end
24
+
25
+ # If the expectation has been called +n+ times, is it still
26
+ # eligible to be called again? The default answer compares n to
27
+ # the established limit.
28
+ def eligible?(n)
29
+ n < @limit
30
+ end
31
+ end
32
+
33
+ ####################################################################
34
+ # Validator for exact call counts.
35
+ #
36
+ class ExactCountValidator < CountValidator
37
+ # Validate that the method expectation was called exactly +n+
38
+ # times.
39
+ def validate(n)
40
+ FlexMock.framework_adapter.assert_equal @limit, n,
41
+ "method '#{@exp}' called incorrect number of times"
42
+ end
43
+ end
44
+
45
+ ####################################################################
46
+ # Validator for call counts greater than or equal to a limit.
47
+ #
48
+ class AtLeastCountValidator < CountValidator
49
+ # Validate the method expectation was called no more than +n+
50
+ # times.
51
+ def validate(n)
52
+ FlexMock.framework_adapter.assert_block(
53
+ "Method '#{@exp}' should be called at least #{@limit} times,\n" +
54
+ "only called #{n} times") { n >= @limit }
55
+ end
56
+
57
+ # If the expectation has been called +n+ times, is it still
58
+ # eligible to be called again? Since this validator only
59
+ # establishes a lower limit, not an upper limit, then the answer
60
+ # is always true.
61
+ def eligible?(n)
62
+ true
63
+ end
64
+ end
65
+
66
+ ####################################################################
67
+ # Validator for call counts less than or equal to a limit.
68
+ #
69
+ class AtMostCountValidator < CountValidator
70
+ # Validate the method expectation was called at least +n+ times.
71
+ def validate(n)
72
+ FlexMock.framework_adapter.assert_block(
73
+ "Method '#{@exp}' should be called at most #{@limit} times,\n" +
74
+ "only called #{n} times") { n <= @limit }
75
+ end
76
+ end
77
+ end