rspec-mocks 2.9.0 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document CHANGED
@@ -1,5 +1,5 @@
1
- README.markdown
2
1
  lib/**/*.rb
3
- bin/*
4
- features/**/*.feature
2
+ -
3
+ README.md
5
4
  License.txt
5
+ Changelog.md
data/.yardopts CHANGED
@@ -1,7 +1,6 @@
1
1
  --exclude features
2
2
  --no-private
3
3
  --markup markdown
4
- lib/**/*.rb
5
4
  -
6
5
  Changelog.md
7
6
  License.txt
@@ -1,3 +1,10 @@
1
+ ### 2.10.0 / 2012-05-03
2
+ [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.9.0...v2.10.0)
3
+
4
+ Bug fixes
5
+
6
+ * fail fast when an `exactly` or `at_most` expectation is exceeded
7
+
1
8
  ### 2.9.0 / 2012-03-17
2
9
  [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.8.0...v2.9.0)
3
10
 
data/README.md CHANGED
@@ -116,12 +116,19 @@ Person.stub(:find) { person }
116
116
  ```
117
117
 
118
118
  In this case we're instrumenting Person to return the person object we've
119
- defined whenever it receives the `find` message. We can do this with any
120
- object in a system because rspec-mocks adds the `stub` and `should_receive`
121
- methods to every object. When we use either, RSpec replaces the method
122
- we're stubbing or mocking with its own test-double-like method. At the
123
- end of the example, RSpec verifies any message expectations, and then
124
- restores the original methods.
119
+ defined whenever it receives the `find` message. We can also set a message
120
+ expectation so that the example fails if `find` is not called:
121
+
122
+ ```ruby
123
+ person = double("person")
124
+ Person.should_receive(:find) { person }
125
+ ```
126
+
127
+ We can do this with any object in a system because rspec-mocks adds the `stub`
128
+ and `should_receive` methods to every object, including class objects. When we
129
+ use either, RSpec replaces the method we're stubbing or mocking with its own
130
+ test-double-like method. At the end of the example, RSpec verifies any message
131
+ expectations, and then restores the original methods.
125
132
 
126
133
  ## Expecting Arguments
127
134
 
@@ -176,7 +183,7 @@ double.should_receive(:other_msg).ordered
176
183
  #This will fail if the messages are received out of order
177
184
  ```
178
185
 
179
- ## Setting Reponses
186
+ ## Setting Responses
180
187
 
181
188
  Whether you are setting a message expectation or a method stub, you can
182
189
  tell the object precisely how to respond. The most generic way is to pass
File without changes
@@ -64,7 +64,7 @@ module RSpec
64
64
  def intro
65
65
  if @name
66
66
  "#{@declared_as} #{@name.inspect}"
67
- elsif Mock === @target
67
+ elsif TestDouble === @target
68
68
  @declared_as
69
69
  elsif Class === @target
70
70
  "<#{@target.inspect} (class)>"
@@ -7,6 +7,7 @@ require 'rspec/mocks/method_double'
7
7
  require 'rspec/mocks/methods'
8
8
  require 'rspec/mocks/argument_matchers'
9
9
  require 'rspec/mocks/proxy'
10
+ require 'rspec/mocks/test_double'
10
11
  require 'rspec/mocks/mock'
11
12
  require 'rspec/mocks/argument_expectation'
12
13
  require 'rspec/mocks/message_expectation'
@@ -4,19 +4,17 @@ module RSpec
4
4
  class MessageExpectation
5
5
  # @private
6
6
  attr_reader :message
7
- attr_writer :expected_received_count, :method_block, :expected_from, :argument_expectation
8
- protected :expected_received_count=, :method_block=, :expected_from=
7
+ attr_writer :expected_received_count, :expected_from, :argument_expectation, :implementation
8
+ protected :expected_received_count=, :expected_from=, :implementation=
9
9
  attr_accessor :error_generator
10
10
  protected :error_generator, :error_generator=
11
11
 
12
12
  # @private
13
- def initialize(error_generator, expectation_ordering, expected_from, message, method_block, expected_received_count=1, opts={}, &implementation)
13
+ def initialize(error_generator, expectation_ordering, expected_from, message, expected_received_count=1, opts={}, &implementation)
14
14
  @error_generator = error_generator
15
15
  @error_generator.opts = opts
16
16
  @expected_from = expected_from
17
17
  @message = message
18
- @method_block = method_block
19
- @return_block = nil
20
18
  @actual_received_count = 0
21
19
  @expected_received_count = expected_received_count
22
20
  @argument_expectation = ArgumentExpectation.new(ArgumentMatchers::AnyArgsMatcher.new)
@@ -24,21 +22,19 @@ module RSpec
24
22
  @exception_to_raise = nil
25
23
  @args_to_throw = []
26
24
  @order_group = expectation_ordering
27
- @at_least = nil
28
- @at_most = nil
29
- @exactly = nil
25
+ @at_least = @at_most = @exactly = nil
30
26
  @args_to_yield = []
31
27
  @failed_fast = nil
32
28
  @args_to_yield_were_cloned = false
33
- @return_block = implementation
34
29
  @eval_context = nil
30
+ @implementation = implementation
35
31
  end
36
32
 
37
33
  # @private
38
- def build_child(expected_from, method_block, expected_received_count, opts={})
34
+ def build_child(expected_from, expected_received_count, opts={}, &implementation)
39
35
  child = clone
40
36
  child.expected_from = expected_from
41
- child.method_block = method_block
37
+ child.implementation = implementation if implementation
42
38
  child.expected_received_count = expected_received_count
43
39
  child.clear_actual_received_count!
44
40
  new_gen = error_generator.clone
@@ -90,18 +86,10 @@ module RSpec
90
86
  # # ... this is prefered
91
87
  # counter.stub(:count) { 1 }
92
88
  # counter.count # => 1
93
- def and_return(*values, &return_block)
94
- Kernel::raise AmbiguousReturnError unless @method_block.nil?
95
- case values.size
96
- when 0 then value = nil
97
- when 1 then value = values[0]
98
- else
99
- value = values
100
- @consecutive = true
101
- @expected_received_count = values.size if !ignoring_args? &&
102
- @expected_received_count < values.size
103
- end
104
- @return_block = block_given? ? return_block : lambda { value }
89
+ def and_return(*values, &implementation)
90
+ @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args?
91
+ @consecutive = true if values.size > 1
92
+ @implementation = implementation || build_implementation(values)
105
93
  end
106
94
 
107
95
  # @overload and_raise
@@ -137,8 +125,7 @@ module RSpec
137
125
  # car.stub(:go).and_throw(:out_of_gas)
138
126
  # car.stub(:go).and_throw(:out_of_gas, :level => 0.1)
139
127
  def and_throw(symbol, object = nil)
140
- @args_to_throw << symbol
141
- @args_to_throw << object if object
128
+ @args_to_throw = [symbol, object].compact
142
129
  end
143
130
 
144
131
  # Tells the object to yield one or more args to a block when the message
@@ -153,11 +140,7 @@ module RSpec
153
140
  @args_to_yield_were_cloned = false
154
141
  end
155
142
 
156
- if block
157
- @eval_context = Object.new
158
- @eval_context.extend RSpec::Mocks::InstanceExec
159
- yield @eval_context
160
- end
143
+ yield @eval_context = Object.new.extend(RSpec::Mocks::InstanceExec) if block
161
144
 
162
145
  @args_to_yield << args
163
146
  self
@@ -165,42 +148,29 @@ module RSpec
165
148
 
166
149
  # @private
167
150
  def matches?(message, *args)
168
- @message == message and @argument_expectation.args_match?(*args)
151
+ @message == message && @argument_expectation.args_match?(*args)
169
152
  end
170
153
 
171
154
  # @private
172
155
  def invoke(*args, &block)
173
- if @expected_received_count == 0
174
- @failed_fast = true
156
+ if @expected_received_count == 0 || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
175
157
  @actual_received_count += 1
158
+ @failed_fast = true
176
159
  @error_generator.raise_expectation_error(@message, @expected_received_count, @actual_received_count, *args)
177
160
  end
178
161
 
179
162
  @order_group.handle_order_constraint self
180
163
 
181
164
  begin
182
- begin
183
- raise(@exception_to_raise) unless @exception_to_raise.nil?
184
- rescue ArgumentError => e
185
- raise e.exception(<<-MESSAGE)
186
- 'and_raise' can only accept an Exception class if an instance can be constructed with no arguments.
187
- #{@exception_to_raise.to_s}'s initialize method requires #{@exception_to_raise.instance_method(:initialize).arity} arguments, so you have to supply an instance instead.
188
- MESSAGE
189
- end
165
+ raise_exception unless @exception_to_raise.nil?
190
166
  Kernel::throw(*@args_to_throw) unless @args_to_throw.empty?
191
167
 
192
- default_return_val = if !@method_block.nil?
193
- invoke_method_block(*args, &block)
194
- elsif !@args_to_yield.empty? || @eval_context
195
- invoke_with_yield(&block)
196
- else
197
- nil
198
- end
168
+ default_return_val = call_with_yield(&block) if !@args_to_yield.empty? || @eval_context
199
169
 
200
170
  if @consecutive
201
- invoke_consecutive_return_block(*args, &block)
202
- elsif @return_block
203
- invoke_return_block(*args, &block)
171
+ call_implementation_consecutive(*args, &block)
172
+ elsif @implementation
173
+ call_implementation(*args, &block)
204
174
  else
205
175
  default_return_val
206
176
  end
@@ -209,10 +179,22 @@ MESSAGE
209
179
  end
210
180
  end
211
181
 
182
+ # @private
183
+ def raise_exception
184
+ if !@exception_to_raise.respond_to?(:instance_method) ||
185
+ @exception_to_raise.instance_method(:initialize).arity <= 0
186
+ raise(@exception_to_raise)
187
+ else
188
+ raise ArgumentError.new(<<-MESSAGE)
189
+ 'and_raise' can only accept an Exception class if an instance can be constructed with no arguments.
190
+ #{@exception_to_raise.to_s}'s initialize method requires #{@exception_to_raise.instance_method(:initialize).arity} arguments, so you have to supply an instance instead.
191
+ MESSAGE
192
+ end
193
+ end
194
+
212
195
  # @private
213
196
  def called_max_times?
214
- @expected_received_count != :any && @expected_received_count > 0 &&
215
- @actual_received_count >= @expected_received_count
197
+ @expected_received_count != :any && @expected_received_count > 0 && @actual_received_count >= @expected_received_count
216
198
  end
217
199
 
218
200
  # @private
@@ -301,7 +283,7 @@ MESSAGE
301
283
  # cart.add(Book.new(:isbn => 1934356379))
302
284
  # # => passes
303
285
  def with(*args, &block)
304
- @return_block = block if block_given? unless args.empty?
286
+ @implementation = block if block_given? unless args.empty?
305
287
  @argument_expectation = ArgumentExpectation.new(*args, &block)
306
288
  self
307
289
  end
@@ -313,7 +295,7 @@ MESSAGE
313
295
  #
314
296
  # dealer.should_recieve(:deal_card).exactly(10).times
315
297
  def exactly(n, &block)
316
- @method_block = block if block
298
+ @implementation = block if block
317
299
  set_expected_received_count :exactly, n
318
300
  self
319
301
  end
@@ -325,7 +307,7 @@ MESSAGE
325
307
  #
326
308
  # dealer.should_recieve(:deal_card).at_least(9).times
327
309
  def at_least(n, &block)
328
- @method_block = block if block
310
+ @implementation = block if block
329
311
  set_expected_received_count :at_least, n
330
312
  self
331
313
  end
@@ -337,7 +319,7 @@ MESSAGE
337
319
  #
338
320
  # dealer.should_recieve(:deal_card).at_most(10).times
339
321
  def at_most(n, &block)
340
- @method_block = block if block
322
+ @implementation = block if block
341
323
  set_expected_received_count :at_most, n
342
324
  self
343
325
  end
@@ -350,14 +332,14 @@ MESSAGE
350
332
  # dealer.should_recieve(:deal_card).at_least(10).times
351
333
  # dealer.should_recieve(:deal_card).at_most(10).times
352
334
  def times(&block)
353
- @method_block = block if block
335
+ @implementation = block if block
354
336
  self
355
337
  end
356
338
 
357
339
 
358
340
  # Allows an expected message to be received any number of times.
359
341
  def any_number_of_times(&block)
360
- @method_block = block if block
342
+ @implementation = block if block
361
343
  @expected_received_count = :any
362
344
  self
363
345
  end
@@ -378,7 +360,7 @@ MESSAGE
378
360
  #
379
361
  # car.should_receive(:go).once
380
362
  def once(&block)
381
- @method_block = block if block
363
+ @implementation = block if block
382
364
  set_expected_received_count :exactly, 1
383
365
  self
384
366
  end
@@ -389,7 +371,7 @@ MESSAGE
389
371
  #
390
372
  # car.should_receive(:go).twice
391
373
  def twice(&block)
392
- @method_block = block if block
374
+ @implementation = block if block
393
375
  set_expected_received_count :exactly, 2
394
376
  self
395
377
  end
@@ -402,7 +384,7 @@ MESSAGE
402
384
  # api.should_receive(:run).ordered
403
385
  # api.should_receive(:finish).ordered
404
386
  def ordered(&block)
405
- @method_block = block if block
387
+ @implementation = block if block
406
388
  @order_group.register(self)
407
389
  @ordered = true
408
390
  self
@@ -425,44 +407,25 @@ MESSAGE
425
407
 
426
408
  protected
427
409
 
428
- def invoke_method_block(*args, &block)
429
- begin
430
- @method_block.call(*args, &block)
431
- rescue => detail
432
- @error_generator.raise_block_failed_error(@message, detail.message)
433
- end
434
- end
435
-
436
- def invoke_with_yield(&block)
437
- if block.nil?
438
- @error_generator.raise_missing_block_error @args_to_yield
439
- end
410
+ def call_with_yield(&block)
411
+ @error_generator.raise_missing_block_error @args_to_yield unless block
440
412
  value = nil
441
- @args_to_yield.each do |args_to_yield_this_time|
442
- if block.arity > -1 && args_to_yield_this_time.length != block.arity
443
- @error_generator.raise_wrong_arity_error args_to_yield_this_time, block.arity
413
+ @args_to_yield.each do |args|
414
+ if block.arity > -1 && args.length != block.arity
415
+ @error_generator.raise_wrong_arity_error args, block.arity
444
416
  end
445
- value = eval_block(*args_to_yield_this_time, &block)
417
+ value = @eval_context ? @eval_context.instance_exec(*args, &block) : block.call(*args)
446
418
  end
447
419
  value
448
420
  end
449
421
 
450
- def eval_block(*args, &block)
451
- if @eval_context
452
- @eval_context.instance_exec(*args, &block)
453
- else
454
- block.call(*args)
455
- end
456
- end
457
-
458
- def invoke_consecutive_return_block(*args, &block)
459
- value = invoke_return_block(*args, &block)
460
- index = [@actual_received_count, value.size-1].min
461
- value[index]
422
+ def call_implementation_consecutive(*args, &block)
423
+ @value ||= call_implementation(*args, &block)
424
+ @value[[@actual_received_count, @value.size-1].min]
462
425
  end
463
426
 
464
- def invoke_return_block(*args, &block)
465
- @return_block.arity == 0 ? @return_block.call(&block) : @return_block.call(*args, &block)
427
+ def call_implementation(*args, &block)
428
+ @implementation.arity == 0 ? @implementation.call(&block) : @implementation.call(*args, &block)
466
429
  end
467
430
 
468
431
  def clone_args_to_yield(*args)
@@ -488,13 +451,20 @@ MESSAGE
488
451
  def clear_actual_received_count!
489
452
  @actual_received_count = 0
490
453
  end
454
+
455
+ private
456
+
457
+ def build_implementation(values)
458
+ value = values.size == 1 ? values.first : values
459
+ lambda { value }
460
+ end
491
461
  end
492
462
 
493
463
  # @private
494
464
  class NegativeMessageExpectation < MessageExpectation
495
465
  # @private
496
- def initialize(error_generator, expectation_ordering, expected_from, message, method_block)
497
- super(error_generator, expectation_ordering, expected_from, message, method_block, 0)
466
+ def initialize(error_generator, expectation_ordering, expected_from, message, &implementation)
467
+ super(error_generator, expectation_ordering, expected_from, message, 0, {}, &implementation)
498
468
  end
499
469
 
500
470
  # @private
@@ -27,7 +27,7 @@ module RSpec
27
27
 
28
28
  # @private
29
29
  def visibility
30
- if Mock === @object
30
+ if TestDouble === @object
31
31
  'public'
32
32
  elsif object_singleton_class.private_method_defined?(@method_name)
33
33
  'private'
@@ -131,12 +131,12 @@ module RSpec
131
131
  end
132
132
 
133
133
  # @private
134
- def add_expectation(error_generator, expectation_ordering, expected_from, opts, &block)
134
+ def add_expectation(error_generator, expectation_ordering, expected_from, opts, &implementation)
135
135
  configure_method
136
136
  expectation = if existing_stub = stubs.first
137
- existing_stub.build_child(expected_from, block, 1, opts)
137
+ existing_stub.build_child(expected_from, 1, opts, &implementation)
138
138
  else
139
- MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, block, 1, opts)
139
+ MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, 1, opts, &implementation)
140
140
  end
141
141
  expectations << expectation
142
142
  expectation
@@ -145,7 +145,7 @@ module RSpec
145
145
  # @private
146
146
  def add_negative_expectation(error_generator, expectation_ordering, expected_from, &implementation)
147
147
  configure_method
148
- expectation = NegativeMessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, implementation)
148
+ expectation = NegativeMessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, &implementation)
149
149
  expectations.unshift expectation
150
150
  expectation
151
151
  end
@@ -153,7 +153,7 @@ module RSpec
153
153
  # @private
154
154
  def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation)
155
155
  configure_method
156
- stub = MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, nil, :any, opts, &implementation)
156
+ stub = MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, :any, opts, &implementation)
157
157
  stubs.unshift stub
158
158
  stub
159
159
  end