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 +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
|