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 +3 -3
- data/.yardopts +0 -1
- data/Changelog.md +7 -0
- data/README.md +14 -7
- data/features/{README.markdown → README.md} +0 -0
- data/lib/rspec/mocks/error_generator.rb +1 -1
- data/lib/rspec/mocks/framework.rb +1 -0
- data/lib/rspec/mocks/message_expectation.rb +64 -94
- data/lib/rspec/mocks/method_double.rb +6 -6
- data/lib/rspec/mocks/methods.rb +1 -1
- data/lib/rspec/mocks/mock.rb +1 -75
- data/lib/rspec/mocks/proxy.rb +1 -3
- data/lib/rspec/mocks/test_double.rb +102 -0
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/at_most_spec.rb +56 -65
- data/spec/rspec/mocks/mock_spec.rb +11 -11
- data/spec/rspec/mocks/multiple_return_value_spec.rb +64 -133
- data/spec/rspec/mocks/once_counts_spec.rb +31 -32
- data/spec/rspec/mocks/precise_counts_spec.rb +38 -28
- data/spec/rspec/mocks/serialization_spec.rb +23 -22
- data/spec/rspec/mocks/test_double_spec.rb +57 -0
- data/spec/rspec/mocks/twice_counts_spec.rb +44 -45
- metadata +68 -75
data/.document
CHANGED
data/.yardopts
CHANGED
data/Changelog.md
CHANGED
@@ -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
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
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
|
@@ -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, :
|
8
|
-
protected :expected_received_count=, :
|
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,
|
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,
|
34
|
+
def build_child(expected_from, expected_received_count, opts={}, &implementation)
|
39
35
|
child = clone
|
40
36
|
child.expected_from = expected_from
|
41
|
-
child.
|
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, &
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
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
|
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
|
-
|
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 !@
|
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
|
-
|
202
|
-
elsif @
|
203
|
-
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
429
|
-
|
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 |
|
442
|
-
if block.arity > -1 &&
|
443
|
-
@error_generator.raise_wrong_arity_error
|
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 =
|
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
|
451
|
-
|
452
|
-
|
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
|
465
|
-
@
|
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,
|
497
|
-
super(error_generator, expectation_ordering, expected_from, message,
|
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
|
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, &
|
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,
|
137
|
+
existing_stub.build_child(expected_from, 1, opts, &implementation)
|
138
138
|
else
|
139
|
-
MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name,
|
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,
|
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
|