flexmock 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/CHANGELOG +10 -1
  2. data/README +390 -209
  3. data/Rakefile +31 -10
  4. data/doc/GoogleExample.rdoc +275 -0
  5. data/doc/releases/flexmock-0.6.0.rdoc +136 -0
  6. data/lib/flexmock.rb +3 -1160
  7. data/lib/flexmock/argument_matchers.rb +57 -0
  8. data/lib/flexmock/argument_types.rb +42 -0
  9. data/lib/flexmock/base.rb +22 -0
  10. data/lib/flexmock/composite.rb +10 -0
  11. data/lib/flexmock/core.rb +206 -0
  12. data/lib/flexmock/core_class_methods.rb +92 -0
  13. data/lib/flexmock/default_framework_adapter.rb +31 -0
  14. data/lib/flexmock/expectation.rb +334 -0
  15. data/lib/flexmock/expectation_director.rb +59 -0
  16. data/lib/flexmock/mock_container.rb +159 -0
  17. data/lib/flexmock/noop.rb +13 -0
  18. data/lib/flexmock/partial_mock.rb +226 -0
  19. data/lib/flexmock/recorder.rb +71 -0
  20. data/lib/flexmock/rspec.rb +34 -0
  21. data/lib/flexmock/test_unit.rb +32 -0
  22. data/lib/flexmock/test_unit_integration.rb +53 -0
  23. data/lib/flexmock/validators.rb +77 -0
  24. data/test/rspec_integration/integration_spec.rb +36 -0
  25. data/test/test_container_methods.rb +119 -0
  26. data/test/test_default_framework_adapter.rb +39 -0
  27. data/test/test_example.rb +1 -1
  28. data/test/test_extended_should_receive.rb +63 -0
  29. data/test/test_mock.rb +1 -1
  30. data/test/test_naming.rb +1 -1
  31. data/test/{test_any_instance.rb → test_new_instances.rb} +15 -8
  32. data/test/{test_stubbing.rb → test_partial_mock.rb} +44 -44
  33. data/test/test_record_mode.rb +1 -1
  34. data/test/test_samples.rb +6 -8
  35. data/test/test_should_receive.rb +7 -3
  36. data/test/test_tu_integration.rb +1 -1
  37. data/test/test_unit_integration/test_auto_test_unit.rb +34 -0
  38. metadata +30 -5
  39. data/test/test_class_interception.rb +0 -140
@@ -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