rspec-mocks 2.0.0.a1

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 (62) hide show
  1. data/.document +5 -0
  2. data/.gitignore +6 -0
  3. data/License.txt +22 -0
  4. data/README.markdown +8 -0
  5. data/Rakefile +50 -0
  6. data/VERSION +1 -0
  7. data/VERSION.yml +5 -0
  8. data/lib/rspec/mocks.rb +201 -0
  9. data/lib/rspec/mocks/argument_expectation.rb +51 -0
  10. data/lib/rspec/mocks/argument_matchers.rb +233 -0
  11. data/lib/rspec/mocks/error_generator.rb +81 -0
  12. data/lib/rspec/mocks/errors.rb +10 -0
  13. data/lib/rspec/mocks/extensions.rb +1 -0
  14. data/lib/rspec/mocks/extensions/object.rb +3 -0
  15. data/lib/rspec/mocks/framework.rb +15 -0
  16. data/lib/rspec/mocks/message_expectation.rb +326 -0
  17. data/lib/rspec/mocks/methods.rb +63 -0
  18. data/lib/rspec/mocks/mock.rb +65 -0
  19. data/lib/rspec/mocks/order_group.rb +29 -0
  20. data/lib/rspec/mocks/proxy.rb +230 -0
  21. data/lib/rspec/mocks/space.rb +28 -0
  22. data/lib/rspec/mocks/spec_methods.rb +39 -0
  23. data/lib/spec/mocks.rb +1 -0
  24. data/spec/rspec/mocks/any_number_of_times_spec.rb +36 -0
  25. data/spec/rspec/mocks/argument_expectation_spec.rb +23 -0
  26. data/spec/rspec/mocks/at_least_spec.rb +97 -0
  27. data/spec/rspec/mocks/at_most_spec.rb +93 -0
  28. data/spec/rspec/mocks/bug_report_10260_spec.rb +8 -0
  29. data/spec/rspec/mocks/bug_report_10263_spec.rb +27 -0
  30. data/spec/rspec/mocks/bug_report_11545_spec.rb +32 -0
  31. data/spec/rspec/mocks/bug_report_15719_spec.rb +29 -0
  32. data/spec/rspec/mocks/bug_report_496_spec.rb +17 -0
  33. data/spec/rspec/mocks/bug_report_600_spec.rb +22 -0
  34. data/spec/rspec/mocks/bug_report_7611_spec.rb +19 -0
  35. data/spec/rspec/mocks/bug_report_7805_spec.rb +22 -0
  36. data/spec/rspec/mocks/bug_report_8165_spec.rb +31 -0
  37. data/spec/rspec/mocks/bug_report_8302_spec.rb +26 -0
  38. data/spec/rspec/mocks/bug_report_830_spec.rb +21 -0
  39. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +95 -0
  40. data/spec/rspec/mocks/hash_including_matcher_spec.rb +90 -0
  41. data/spec/rspec/mocks/hash_not_including_matcher_spec.rb +67 -0
  42. data/spec/rspec/mocks/mock_ordering_spec.rb +94 -0
  43. data/spec/rspec/mocks/mock_space_spec.rb +54 -0
  44. data/spec/rspec/mocks/mock_spec.rb +583 -0
  45. data/spec/rspec/mocks/multiple_return_value_spec.rb +113 -0
  46. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +63 -0
  47. data/spec/rspec/mocks/null_object_mock_spec.rb +54 -0
  48. data/spec/rspec/mocks/once_counts_spec.rb +53 -0
  49. data/spec/rspec/mocks/options_hash_spec.rb +35 -0
  50. data/spec/rspec/mocks/partial_mock_spec.rb +164 -0
  51. data/spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb +66 -0
  52. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +145 -0
  53. data/spec/rspec/mocks/precise_counts_spec.rb +52 -0
  54. data/spec/rspec/mocks/record_messages_spec.rb +26 -0
  55. data/spec/rspec/mocks/stub_chain_spec.rb +34 -0
  56. data/spec/rspec/mocks/stub_implementation_spec.rb +31 -0
  57. data/spec/rspec/mocks/stub_spec.rb +198 -0
  58. data/spec/rspec/mocks/stubbed_message_expectations_spec.rb +26 -0
  59. data/spec/rspec/mocks/twice_counts_spec.rb +67 -0
  60. data/spec/spec_helper.rb +52 -0
  61. data/spec/support/macros.rb +29 -0
  62. metadata +172 -0
@@ -0,0 +1,81 @@
1
+ module Rspec
2
+ module Mocks
3
+ class ErrorGenerator
4
+ attr_writer :opts
5
+
6
+ def initialize(target, name)
7
+ @target = target
8
+ @name = name
9
+ end
10
+
11
+ def opts
12
+ @opts ||= {}
13
+ end
14
+
15
+ def raise_unexpected_message_error(sym, *args)
16
+ __raise "#{intro} received unexpected message :#{sym}#{arg_message(*args)}"
17
+ end
18
+
19
+ def raise_unexpected_message_args_error(expectation, *args)
20
+ expected_args = format_args(*expectation.expected_args)
21
+ actual_args = args.empty? ? "(no args)" : format_args(*args)
22
+ __raise "#{intro} expected #{expectation.sym.inspect} with #{expected_args} but received it with #{actual_args}"
23
+ end
24
+
25
+ def raise_expectation_error(sym, expected_received_count, actual_received_count, *args)
26
+ __raise "#{intro} expected :#{sym}#{arg_message(*args)} #{count_message(expected_received_count)}, but received it #{count_message(actual_received_count)}"
27
+ end
28
+
29
+ def raise_out_of_order_error(sym)
30
+ __raise "#{intro} received :#{sym} out of order"
31
+ end
32
+
33
+ def raise_block_failed_error(sym, detail)
34
+ __raise "#{intro} received :#{sym} but passed block failed with: #{detail}"
35
+ end
36
+
37
+ def raise_missing_block_error(args_to_yield)
38
+ __raise "#{intro} asked to yield |#{arg_list(*args_to_yield)}| but no block was passed"
39
+ end
40
+
41
+ def raise_wrong_arity_error(args_to_yield, arity)
42
+ __raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with arity of #{arity}"
43
+ end
44
+
45
+ private
46
+
47
+ def intro
48
+ @name ? "Mock '#{@name}'" : @target.class == Class ? "<#{@target.inspect} (class)>" : (@target.nil? ? "nil" : @target)
49
+ end
50
+
51
+ def __raise(message)
52
+ message = opts[:message] unless opts[:message].nil?
53
+ Kernel::raise(Rspec::Mocks::MockExpectationError, message)
54
+ end
55
+
56
+ def arg_message(*args)
57
+ " with " + format_args(*args)
58
+ end
59
+
60
+ def format_args(*args)
61
+ args.empty? ? "(no args)" : "(" + arg_list(*args) + ")"
62
+ end
63
+
64
+ def arg_list(*args)
65
+ args.collect {|arg| arg.respond_to?(:description) ? arg.description : arg.inspect}.join(", ")
66
+ end
67
+
68
+ def count_message(count)
69
+ return "at least #{pretty_print(count.abs)}" if count < 0
70
+ return pretty_print(count)
71
+ end
72
+
73
+ def pretty_print(count)
74
+ return "once" if count == 1
75
+ return "twice" if count == 2
76
+ return "#{count} times"
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,10 @@
1
+ module Rspec
2
+ module Mocks
3
+ class MockExpectationError < Exception
4
+ end
5
+
6
+ class AmbiguousReturnError < StandardError
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1 @@
1
+ require 'rspec/mocks/extensions/object'
@@ -0,0 +1,3 @@
1
+ class Object
2
+ include Rspec::Mocks::Methods
3
+ end
@@ -0,0 +1,15 @@
1
+ # Require everything except the global extensions of class and object. This
2
+ # supports wrapping rspec's mocking functionality without invading every
3
+ # object in the system.
4
+
5
+ require 'rspec/mocks/methods'
6
+ require 'rspec/mocks/argument_matchers'
7
+ require 'rspec/mocks/spec_methods'
8
+ require 'rspec/mocks/proxy'
9
+ require 'rspec/mocks/mock'
10
+ require 'rspec/mocks/argument_expectation'
11
+ require 'rspec/mocks/message_expectation'
12
+ require 'rspec/mocks/order_group'
13
+ require 'rspec/mocks/errors'
14
+ require 'rspec/mocks/error_generator'
15
+ require 'rspec/mocks/space'
@@ -0,0 +1,326 @@
1
+ module Rspec
2
+ module Mocks
3
+
4
+ class BaseExpectation
5
+ attr_reader :sym
6
+ attr_writer :expected_received_count, :method_block, :expected_from
7
+ protected :expected_received_count=, :method_block=, :expected_from=
8
+ attr_accessor :error_generator
9
+ protected :error_generator, :error_generator=
10
+
11
+ def initialize(error_generator, expectation_ordering, expected_from, sym, method_block, expected_received_count=1, opts={}, &implementation)
12
+ @error_generator = error_generator
13
+ @error_generator.opts = opts
14
+ @expected_from = expected_from
15
+ @sym = sym
16
+ @method_block = method_block
17
+ @return_block = nil
18
+ @actual_received_count = 0
19
+ @expected_received_count = expected_received_count
20
+ @args_expectation = ArgumentExpectation.new([ArgumentMatchers::AnyArgsMatcher.new])
21
+ @consecutive = false
22
+ @exception_to_raise = nil
23
+ @symbol_to_throw = nil
24
+ @order_group = expectation_ordering
25
+ @at_least = nil
26
+ @at_most = nil
27
+ @args_to_yield = []
28
+ @failed_fast = nil
29
+ @args_to_yield_were_cloned = false
30
+ @return_block = implementation
31
+ end
32
+
33
+ def build_child(expected_from, method_block, expected_received_count, opts={})
34
+ child = clone
35
+ child.expected_from = expected_from
36
+ child.method_block = method_block
37
+ child.expected_received_count = expected_received_count
38
+ child.clear_actual_received_count!
39
+ new_gen = error_generator.clone
40
+ new_gen.opts = opts
41
+ child.error_generator = new_gen
42
+ child.clone_args_to_yield @args_to_yield
43
+ child
44
+ end
45
+
46
+ def expected_args
47
+ @args_expectation.args
48
+ end
49
+
50
+ def and_return(*values, &return_block)
51
+ Kernel::raise AmbiguousReturnError unless @method_block.nil?
52
+ case values.size
53
+ when 0 then value = nil
54
+ when 1 then value = values[0]
55
+ else
56
+ value = values
57
+ @consecutive = true
58
+ @expected_received_count = values.size if !ignoring_args? &&
59
+ @expected_received_count < values.size
60
+ end
61
+ @return_block = block_given? ? return_block : lambda { value }
62
+ end
63
+
64
+ # :call-seq:
65
+ # and_raise()
66
+ # and_raise(Exception) #any exception class
67
+ # and_raise(exception) #any exception object
68
+ #
69
+ # == Warning
70
+ #
71
+ # When you pass an exception class, the MessageExpectation will
72
+ # raise an instance of it, creating it with +new+. If the exception
73
+ # class initializer requires any parameters, you must pass in an
74
+ # instance and not the class.
75
+ def and_raise(exception=Exception)
76
+ @exception_to_raise = exception
77
+ end
78
+
79
+ def and_throw(symbol)
80
+ @symbol_to_throw = symbol
81
+ end
82
+
83
+ def and_yield(*args)
84
+ if @args_to_yield_were_cloned
85
+ @args_to_yield.clear
86
+ @args_to_yield_were_cloned = false
87
+ end
88
+
89
+ @args_to_yield << args
90
+ self
91
+ end
92
+
93
+ def matches(sym, args)
94
+ @sym == sym and @args_expectation.args_match?(args)
95
+ end
96
+
97
+ def invoke(args, block)
98
+ if @expected_received_count == 0
99
+ @failed_fast = true
100
+ @actual_received_count += 1
101
+ @error_generator.raise_expectation_error @sym, @expected_received_count, @actual_received_count, *args
102
+ end
103
+
104
+ @order_group.handle_order_constraint self
105
+
106
+ begin
107
+ Kernel::raise @exception_to_raise unless @exception_to_raise.nil?
108
+ Kernel::throw @symbol_to_throw unless @symbol_to_throw.nil?
109
+
110
+
111
+ if !@method_block.nil?
112
+ default_return_val = invoke_method_block(args)
113
+ elsif @args_to_yield.size > 0
114
+ default_return_val = invoke_with_yield(block)
115
+ else
116
+ default_return_val = nil
117
+ end
118
+
119
+ if @consecutive
120
+ return invoke_consecutive_return_block(args, block)
121
+ elsif @return_block
122
+ return invoke_return_block(args, block)
123
+ else
124
+ return default_return_val
125
+ end
126
+ ensure
127
+ @actual_received_count += 1
128
+ end
129
+ end
130
+
131
+ def called_max_times?
132
+ @expected_received_count != :any && @expected_received_count > 0 &&
133
+ @actual_received_count >= @expected_received_count
134
+ end
135
+
136
+ protected
137
+
138
+ def invoke_method_block(args)
139
+ begin
140
+ @method_block.call(*args)
141
+ rescue => detail
142
+ @error_generator.raise_block_failed_error @sym, detail.message
143
+ end
144
+ end
145
+
146
+ def invoke_with_yield(block)
147
+ if block.nil?
148
+ @error_generator.raise_missing_block_error @args_to_yield
149
+ end
150
+ value = nil
151
+ @args_to_yield.each do |args_to_yield_this_time|
152
+ if block.arity > -1 && args_to_yield_this_time.length != block.arity
153
+ @error_generator.raise_wrong_arity_error args_to_yield_this_time, block.arity
154
+ end
155
+ value = block.call(*args_to_yield_this_time)
156
+ end
157
+ value
158
+ end
159
+
160
+ def invoke_consecutive_return_block(args, block)
161
+ value = invoke_return_block(args, block)
162
+ index = [@actual_received_count, value.size-1].min
163
+ value[index]
164
+ end
165
+
166
+ def invoke_return_block(args, block)
167
+ args << block unless block.nil?
168
+ # Ruby 1.9 - when we set @return_block to return values
169
+ # regardless of arguments, any arguments will result in
170
+ # a "wrong number of arguments" error
171
+ @return_block.arity == 0 ? @return_block.call : @return_block.call(*args)
172
+ end
173
+
174
+ def clone_args_to_yield(args)
175
+ @args_to_yield = args.clone
176
+ @args_to_yield_were_cloned = true
177
+ end
178
+
179
+ def failed_fast?
180
+ @failed_fast
181
+ end
182
+ end
183
+
184
+ class MessageExpectation < BaseExpectation
185
+
186
+ def matches_name_but_not_args(sym, args)
187
+ @sym == sym and not @args_expectation.args_match?(args)
188
+ end
189
+
190
+ def verify_messages_received
191
+ return if expected_messages_received? || failed_fast?
192
+
193
+ generate_error
194
+ rescue Rspec::Mocks::MockExpectationError => error
195
+ error.backtrace.insert(0, @expected_from)
196
+ Kernel::raise error
197
+ end
198
+
199
+ def expected_messages_received?
200
+ ignoring_args? || matches_exact_count? ||
201
+ matches_at_least_count? || matches_at_most_count?
202
+ end
203
+
204
+ def ignoring_args?
205
+ @expected_received_count == :any
206
+ end
207
+
208
+ def matches_at_least_count?
209
+ @at_least && @actual_received_count >= @expected_received_count
210
+ end
211
+
212
+ def matches_at_most_count?
213
+ @at_most && @actual_received_count <= @expected_received_count
214
+ end
215
+
216
+ def matches_exact_count?
217
+ @expected_received_count == @actual_received_count
218
+ end
219
+
220
+ def similar_messages
221
+ @similar_messages ||= []
222
+ end
223
+
224
+ def advise(args, block)
225
+ similar_messages << args
226
+ end
227
+
228
+ def generate_error
229
+ if similar_messages.empty?
230
+ @error_generator.raise_expectation_error(@sym, @expected_received_count, @actual_received_count, *@args_expectation.args)
231
+ else
232
+ @error_generator.raise_unexpected_message_args_error(self, *@similar_messages)
233
+ end
234
+ end
235
+
236
+ def with(*args, &block)
237
+ @args_expectation = ArgumentExpectation.new(args, &block)
238
+ self
239
+ end
240
+
241
+ def exactly(n)
242
+ set_expected_received_count :exactly, n
243
+ self
244
+ end
245
+
246
+ def at_least(n)
247
+ set_expected_received_count :at_least, n
248
+ self
249
+ end
250
+
251
+ def at_most(n)
252
+ set_expected_received_count :at_most, n
253
+ self
254
+ end
255
+
256
+ def times(&block)
257
+ @method_block = block if block
258
+ self
259
+ end
260
+
261
+ def any_number_of_times(&block)
262
+ @method_block = block if block
263
+ @expected_received_count = :any
264
+ self
265
+ end
266
+
267
+ def never
268
+ @expected_received_count = 0
269
+ self
270
+ end
271
+
272
+ def once(&block)
273
+ @method_block = block if block
274
+ @expected_received_count = 1
275
+ self
276
+ end
277
+
278
+ def twice(&block)
279
+ @method_block = block if block
280
+ @expected_received_count = 2
281
+ self
282
+ end
283
+
284
+ def ordered(&block)
285
+ @method_block = block if block
286
+ @order_group.register(self)
287
+ @ordered = true
288
+ self
289
+ end
290
+
291
+ def negative_expectation_for?(sym)
292
+ return false
293
+ end
294
+
295
+ protected
296
+ def set_expected_received_count(relativity, n)
297
+ @at_least = (relativity == :at_least)
298
+ @at_most = (relativity == :at_most)
299
+ @expected_received_count = case n
300
+ when Numeric
301
+ n
302
+ when :once
303
+ 1
304
+ when :twice
305
+ 2
306
+ end
307
+ end
308
+
309
+ def clear_actual_received_count!
310
+ @actual_received_count = 0
311
+ end
312
+
313
+ end
314
+
315
+ class NegativeMessageExpectation < MessageExpectation
316
+ def initialize(message, expectation_ordering, expected_from, sym, method_block)
317
+ super(message, expectation_ordering, expected_from, sym, method_block, 0)
318
+ end
319
+
320
+ def negative_expectation_for?(sym)
321
+ return @sym == sym
322
+ end
323
+ end
324
+
325
+ end
326
+ end
@@ -0,0 +1,63 @@
1
+ module Rspec
2
+ module Mocks
3
+ module Methods
4
+ def should_receive(sym, opts={}, &block)
5
+ __mock_proxy.add_message_expectation(opts[:expected_from] || caller(1)[0], sym.to_sym, opts, &block)
6
+ end
7
+
8
+ def should_not_receive(sym, &block)
9
+ __mock_proxy.add_negative_message_expectation(caller(1)[0], sym.to_sym, &block)
10
+ end
11
+
12
+ def stub!(sym_or_hash, opts={}, &block)
13
+ if Hash === sym_or_hash
14
+ sym_or_hash.each {|method, value| stub!(method).and_return value }
15
+ else
16
+ __mock_proxy.add_stub(caller(1)[0], sym_or_hash.to_sym, opts, &block)
17
+ end
18
+ end
19
+
20
+ alias_method :stub, :stub!
21
+
22
+ def stub_chain(*methods)
23
+ if methods.length > 1
24
+ next_in_chain = Object.new
25
+ stub!(methods.shift) {next_in_chain}
26
+ next_in_chain.stub_chain(*methods)
27
+ else
28
+ stub!(methods.shift)
29
+ end
30
+ end
31
+
32
+ def received_message?(sym, *args, &block) #:nodoc:
33
+ __mock_proxy.received_message?(sym.to_sym, *args, &block)
34
+ end
35
+
36
+ def rspec_verify #:nodoc:
37
+ __mock_proxy.verify
38
+ end
39
+
40
+ def rspec_reset #:nodoc:
41
+ __mock_proxy.reset
42
+ end
43
+
44
+ def as_null_object
45
+ __mock_proxy.as_null_object
46
+ end
47
+
48
+ def null_object?
49
+ __mock_proxy.null_object?
50
+ end
51
+
52
+ private
53
+
54
+ def __mock_proxy
55
+ if Mock === self
56
+ @mock_proxy ||= Proxy.new(self, @name, @options)
57
+ else
58
+ @mock_proxy ||= Proxy.new(self)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end