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,334 @@
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
+ # An Expectation is returned from each +should_receive+ message sent
18
+ # to mock object. Each expectation records how a message matching
19
+ # the message name (argument to +should_receive+) and the argument
20
+ # list (given by +with+) should behave. Mock expectations can be
21
+ # recorded by chaining the declaration methods defined in this
22
+ # class.
23
+ #
24
+ # For example:
25
+ #
26
+ # mock.should_receive(:meth).with(args).and_returns(result)
27
+ #
28
+ class Expectation
29
+
30
+ attr_reader :expected_args, :order_number
31
+ attr_accessor :mock
32
+
33
+ # Create an expectation for a method named +sym+.
34
+ def initialize(mock, sym)
35
+ @mock = mock
36
+ @sym = sym
37
+ @expected_args = nil
38
+ @count_validators = []
39
+ @count_validator_class = ExactCountValidator
40
+ @actual_count = 0
41
+ @return_value = nil
42
+ @return_block = lambda { @return_value }
43
+ @order_number = nil
44
+ end
45
+
46
+ def to_s
47
+ FlexMock.format_args(@sym, @expected_args)
48
+ end
49
+
50
+ # Verify the current call with the given arguments matches the
51
+ # expectations recorded in this object.
52
+ def verify_call(*args)
53
+ validate_order
54
+ @actual_count += 1
55
+ @return_block.call(*args)
56
+ end
57
+
58
+ # Is this expectation eligible to be called again? It is eligible
59
+ # only if all of its count validators agree that it is eligible.
60
+ def eligible?
61
+ @count_validators.all? { |v| v.eligible?(@actual_count) }
62
+ end
63
+
64
+ # Validate that the order
65
+ def validate_order
66
+ return if @order_number.nil?
67
+ FlexMock.check("method #{to_s} called out of order " +
68
+ "(expected order #{@order_number}, was #{@mock.mock_current_order})") {
69
+ @order_number >= @mock.mock_current_order
70
+ }
71
+ @mock.mock_current_order = @order_number
72
+ end
73
+ private :validate_order
74
+
75
+ # Validate the correct number of calls have been made. Called by
76
+ # the teardown process.
77
+ def mock_verify
78
+ @count_validators.each do |v|
79
+ v.validate(@actual_count)
80
+ end
81
+ end
82
+
83
+ # Does the argument list match this expectation's argument
84
+ # specification.
85
+ def match_args(args)
86
+ # TODO: Rethink this:
87
+ # return false if @expected_args.nil?
88
+ return true if @expected_args.nil?
89
+ return false if args.size != @expected_args.size
90
+ (0...args.size).all? { |i| match_arg(@expected_args[i], args[i]) }
91
+ end
92
+
93
+ # Does the expected argument match the corresponding actual value.
94
+ def match_arg(expected, actual)
95
+ expected === actual ||
96
+ expected == actual ||
97
+ ( Regexp === expected && expected === actual.to_s )
98
+ end
99
+
100
+ # Declare that the method should expect the given argument list.
101
+ def with(*args)
102
+ @expected_args = args
103
+ self
104
+ end
105
+
106
+ # Declare that the method should be called with no arguments.
107
+ def with_no_args
108
+ with
109
+ end
110
+
111
+ # Declare that the method can be called with any number of
112
+ # arguments of any type.
113
+ def with_any_args
114
+ @expected_args = nil
115
+ self
116
+ end
117
+
118
+ # :call-seq:
119
+ # and_return(value)
120
+ # and_return(value, value, ...)
121
+ # and_return { |*args| code }
122
+ #
123
+ # Declare that the method returns a particular value (when the
124
+ # argument list is matched).
125
+ #
126
+ # * If a single value is given, it will be returned for all matching
127
+ # calls.
128
+ # * If multiple values are given, each value will be returned in turn for
129
+ # each successive call. If the number of matching calls is greater
130
+ # than the number of values, the last value will be returned for
131
+ # the extra matching calls.
132
+ # * If a block is given, it is evaluated on each call and its
133
+ # value is returned.
134
+ #
135
+ # For example:
136
+ #
137
+ # mock.should_receive(:f).returns(12) # returns 12
138
+ #
139
+ # mock.should_receive(:f).with(String). # returns an
140
+ # returns { |str| str.upcase } # upcased string
141
+ #
142
+ # +and_return+ is an alias for +returns+.
143
+ #
144
+ def and_return(*args, &block)
145
+ @return_block =
146
+ if block_given?
147
+ block
148
+ else
149
+ lambda { args.size == 1 ? args.first : args.shift }
150
+ end
151
+ self
152
+ end
153
+ alias :returns :and_return # :nodoc:
154
+
155
+ # Declare that the method may be called any number of times.
156
+ def zero_or_more_times
157
+ at_least.never
158
+ end
159
+
160
+ # Declare that the method is called +limit+ times with the
161
+ # declared argument list. This may be modified by the +at_least+
162
+ # and +at_most+ declarators.
163
+ def times(limit)
164
+ @count_validators << @count_validator_class.new(self, limit) unless limit.nil?
165
+ @count_validator_class = ExactCountValidator
166
+ self
167
+ end
168
+
169
+ # Declare that the method is never expected to be called with the
170
+ # given argument list. This may be modified by the +at_least+ and
171
+ # +at_most+ declarators.
172
+ def never
173
+ times(0)
174
+ end
175
+
176
+ # Declare that the method is expected to be called exactly once
177
+ # with the given argument list. This may be modified by the
178
+ # +at_least+ and +at_most+ declarators.
179
+ def once
180
+ times(1)
181
+ end
182
+
183
+ # Declare that the method is expected to be called exactly twice
184
+ # with the given argument list. This may be modified by the
185
+ # +at_least+ and +at_most+ declarators.
186
+ def twice
187
+ times(2)
188
+ end
189
+
190
+ # Modifies the next call count declarator (+times+, +never+,
191
+ # +once+ or +twice+) so that the declarator means the method is
192
+ # called at least that many times.
193
+ #
194
+ # E.g. method f must be called at least twice:
195
+ #
196
+ # mock.should_receive(:f).at_least.twice
197
+ #
198
+ def at_least
199
+ @count_validator_class = AtLeastCountValidator
200
+ self
201
+ end
202
+
203
+ # Modifies the next call count declarator (+times+, +never+,
204
+ # +once+ or +twice+) so that the declarator means the method is
205
+ # called at most that many times.
206
+ #
207
+ # E.g. method f must be called no more than twice
208
+ #
209
+ # mock.should_receive(:f).at_most.twice
210
+ #
211
+ def at_most
212
+ @count_validator_class = AtMostCountValidator
213
+ self
214
+ end
215
+
216
+ # Declare that the given method must be called in order. All
217
+ # ordered method calls must be received in the order specified by
218
+ # the ordering of the +should_receive+ messages. Receiving a
219
+ # methods out of the specified order will cause a test failure.
220
+ #
221
+ # If the user needs more fine control over ordering
222
+ # (e.g. specifying that a group of messages may be received in any
223
+ # order as long as they all come after another group of messages),
224
+ # a _group_ _name_ may be specified in the +ordered+ calls. All
225
+ # messages within the same group may be received in any order.
226
+ #
227
+ # For example, in the following, messages +flip+ and +flop+ may be
228
+ # received in any order (because they are in the same group), but
229
+ # must occur strictly after +start+ but before +end+. The message
230
+ # +any_time+ may be received at any time because it is not
231
+ # ordered.
232
+ #
233
+ # m = FlexMock.new
234
+ # m.should_receive(:any_time)
235
+ # m.should_receive(:start).ordered
236
+ # m.should_receive(:flip).ordered(:flip_flop_group)
237
+ # m.should_receive(:flop).ordered(:flip_flop_group)
238
+ # m.should_receive(:end).ordered
239
+ #
240
+ def ordered(group_name=nil)
241
+ if group_name.nil?
242
+ @order_number = @mock.mock_allocate_order
243
+ elsif (num = @mock.mock_groups[group_name])
244
+ @order_number = num
245
+ else
246
+ @order_number = @mock.mock_allocate_order
247
+ @mock.mock_groups[group_name] = @order_number
248
+ end
249
+ self
250
+ end
251
+ end
252
+
253
+ ##########################################################################
254
+ # A composite expectation allows several expectations to be grouped into a
255
+ # single composite and then apply the same constraints to all expectations
256
+ # in the group.
257
+ class CompositeExpectation
258
+
259
+ # Initialize the composite expectation.
260
+ def initialize
261
+ @expectations = []
262
+ end
263
+
264
+ # Add an expectation to the composite.
265
+ def add(expectation)
266
+ @expectations << expectation
267
+ end
268
+
269
+ # Apply the constraint method to all expectations in the composite.
270
+ def method_missing(sym, *args, &block)
271
+ @expectations.each do |expectation|
272
+ expectation.send(sym, *args, &block)
273
+ end
274
+ self
275
+ end
276
+
277
+ # The following methods return a value, so we make an arbitrary choice
278
+ # and return the value for the first expectation in the composite.
279
+
280
+ # Return the order number of the first expectation in the list.
281
+ def order_number
282
+ @expectations.first.order_number
283
+ end
284
+
285
+ # Return the associated mock object.
286
+ def mock
287
+ @expectations.first.mock
288
+ end
289
+
290
+ # Start a new method expectation. The following constraints will be
291
+ # applied to the new expectation.
292
+ def should_receive(*args, &block)
293
+ @expectations.first.mock.should_receive(*args, &block)
294
+ end
295
+
296
+ # Return a string representations
297
+ def to_s
298
+ if @expectations.size > 1
299
+ "[" + @expectations.collect { |e| e.to_s }.join(', ') + "]"
300
+ else
301
+ @expectations.first.to_s
302
+ end
303
+ end
304
+ end
305
+
306
+ ##########################################################################
307
+ # An expectation recorder records any expectations received and plays them
308
+ # back on demand. This is used to collect the expectations in the blockless
309
+ # version of the new_instances call.
310
+ #
311
+ class ExpectationRecorder
312
+
313
+ # Initialize the recorder.
314
+ def initialize
315
+ @expectations = []
316
+ end
317
+
318
+ # Save any incoming messages to be played back later.
319
+ def method_missing(sym, *args, &block)
320
+ @expectations << [sym, args, block]
321
+ self
322
+ end
323
+
324
+ # Apply the recorded messages to the given object in a chaining fashion
325
+ # (i.e. the result of the previous call is used as the target of the next
326
+ # call).
327
+ def apply(mock)
328
+ obj = mock
329
+ @expectations.each do |sym, args, block|
330
+ obj = obj.send(sym, *args, &block)
331
+ end
332
+ end
333
+ end
334
+ end
@@ -0,0 +1,59 @@
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
+ # The expectation director is responsible for routing calls to the
18
+ # correct expectations for a given argument list.
19
+ #
20
+ class ExpectationDirector
21
+
22
+ # Create an ExpectationDirector for a mock object.
23
+ def initialize(sym)
24
+ @sym = sym
25
+ @expectations = []
26
+ @expected_order = nil
27
+ end
28
+
29
+ # Invoke the expectations for a given set of arguments.
30
+ #
31
+ # First, look for an expectation that matches the arguements and
32
+ # is eligible to be called. Failing that, look for a expectation
33
+ # that matches the arguments (at this point it will be ineligible,
34
+ # but at least we will get a good failure message). Finally,
35
+ # check for expectations that don't have any argument matching
36
+ # criteria.
37
+ def call(*args)
38
+ exp = @expectations.find { |e| e.match_args(args) && e.eligible? } ||
39
+ @expectations.find { |e| e.match_args(args) }
40
+ FlexMock.check("no matching handler found for " +
41
+ FlexMock.format_args(@sym, args)) { ! exp.nil? }
42
+ exp.verify_call(*args)
43
+ end
44
+
45
+ # Append an expectation to this director.
46
+ def <<(expectation)
47
+ @expectations << expectation
48
+ end
49
+
50
+ # Do the post test verification for this directory. Check all the
51
+ # expectations.
52
+ def mock_verify
53
+ @expectations.each do |exp|
54
+ exp.mock_verify
55
+ end
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,159 @@
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
+ # Mock container methods
18
+ #
19
+ # Include this module in to get integration with FlexMock. When
20
+ # this module is included, mocks may be created with a simple call
21
+ # to the +flexmock+ method. Mocks created with via the method call
22
+ # will automatically be verified in the teardown of the test case.
23
+ #
24
+ module MockContainer
25
+ # Do the flexmock specific teardown stuff. If you need finer control,
26
+ # you can use either +flexmock_verify+ or +flexmock_close+.
27
+ def flexmock_teardown
28
+ flexmock_verify if passed?
29
+ ensure
30
+ flexmock_close
31
+ end
32
+
33
+ # Perform verification on all mocks in the container.
34
+ def flexmock_verify
35
+ @flexmock_created_mocks ||= []
36
+ @flexmock_created_mocks.each do |m|
37
+ m.mock_verify
38
+ end
39
+ end
40
+
41
+ # Close all the mock objects in the container. Closing a mock object
42
+ # restores any original behavior that was displaced by the mock.
43
+ def flexmock_close
44
+ @flexmock_created_mocks ||= []
45
+ @flexmock_created_mocks.each do |m|
46
+ m.mock_teardown
47
+ end
48
+ @flexmock_created_mocks = []
49
+ end
50
+
51
+ # Create a mocking object in the FlexMock framework. The +flexmock+
52
+ # method has a number of options available, depending on just what
53
+ # kind of mocking object your require. Mocks created via +flexmock+
54
+ # will be automatically verify during the teardown phase of your
55
+ # test framework.
56
+ #
57
+ # :call-seq:
58
+ # flexmock() { |mock| ... }
59
+ # flexmock(name) { |mock| ... }
60
+ # flexmock(expect_hash) { |mock| ... }
61
+ # flexmock(name, expect_hash) { |mock| ... }
62
+ # flexmock(real_object) { |mock| ... }
63
+ # flexmock(real_object, name) { |mock| ... }
64
+ # flexmock(real_object, name, expect_hash) { |mock| ... }
65
+ # flexmock(:base, string, name, expect_hash) { |mock| ... }
66
+ #
67
+ # name ::
68
+ # Name of the mock object. If no name is given, "unknown" is used for
69
+ # full mocks and "flexmock(<em>real_object</em>)" is used for partial
70
+ # mocks.
71
+ #
72
+ # expect_hash ::
73
+ # Hash table of method names and values. Each method/value pair is
74
+ # used to setup a simple expectation so that if the mock object
75
+ # receives a message matching an entry in the table, it returns
76
+ # the associated value. No argument our call count constraints are
77
+ # added. Using an expect_hash is identical to calling:
78
+ #
79
+ # mock.should_receive(method_name).and_return(value)
80
+ #
81
+ # for each of the method/value pairs in the hash.
82
+ #
83
+ # real_object ::
84
+ # If a real object is given, then a partial mock is constructed
85
+ # using the real_object as a base. Partial mocks (formally referred
86
+ # to as stubs) behave as a mock object when an expectation is matched,
87
+ # and otherwise will behave like the original object. This is useful
88
+ # when you want to use a real object for testing, but need to mock out
89
+ # just one or two methods.
90
+ #
91
+ # :base ::
92
+ # Forces the following argument to be used as the base of a
93
+ # partial mock object. This explicit tag is only needed if you
94
+ # want to use a string or a symbol as the mock base (string and
95
+ # symbols would normally be interpretted as the mock name).
96
+ #
97
+ # &block ::
98
+ # If a block is given, then the mock object is passed to the block and
99
+ # expectations may be configured within the block.
100
+ #
101
+ def flexmock(*args)
102
+ name = nil
103
+ quick_defs = {}
104
+ stub_target = nil
105
+ while ! args.empty?
106
+ case args.first
107
+ when :base
108
+ args.shift
109
+ stub_target = args.shift
110
+ when String, Symbol
111
+ name = args.shift.to_s
112
+ when Hash
113
+ quick_defs = args.shift
114
+ else
115
+ stub_target = args.shift
116
+ end
117
+ end
118
+ if stub_target
119
+ mock = flexmock_make_stub(stub_target, name)
120
+ else
121
+ mock = FlexMock.new(name || "unknown")
122
+ end
123
+ flexmock_quick_define(mock, quick_defs)
124
+ yield(mock) if block_given?
125
+ flexmock_remember(mock)
126
+ mock
127
+ end
128
+ alias flexstub flexmock
129
+
130
+ private
131
+
132
+ # Create a PartialMock for the given object. Use +name+ as the name
133
+ # of the mock object.
134
+ def flexmock_make_stub(obj, name)
135
+ name ||= "flexmock(#{obj.class.to_s})"
136
+ obj.instance_eval {
137
+ @flexmock_proxy ||= PartialMock.new(obj, FlexMock.new(name))
138
+ }
139
+ obj.instance_variable_get("@flexmock_proxy")
140
+ end
141
+
142
+ # Create a set of mocked methods from a hash.
143
+ def flexmock_quick_define(mock, defs)
144
+ defs.each do |method, value|
145
+ mock.should_receive(method).and_return(value)
146
+ end
147
+ mock
148
+ end
149
+
150
+ # Remember the mock object / stub in the mock container.
151
+ def flexmock_remember(mocking_object)
152
+ @flexmock_created_mocks ||= []
153
+ @flexmock_created_mocks << mocking_object
154
+ mocking_object.mock_container = self
155
+ mocking_object
156
+ end
157
+ end
158
+
159
+ end