rspec-mocks 2.0.0.a1

Sign up to get free protection for your applications and to get access to all the features.
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