rspec-mocks 2.9.0 → 2.10.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.
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