rspec-mocks 2.8.0.rc1 → 2.8.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- data/License.txt +23 -0
- data/README.md +238 -21
- data/lib/rspec/mocks.rb +16 -173
- data/lib/rspec/mocks/any_instance.rb +19 -3
- data/lib/rspec/mocks/any_instance/chain.rb +115 -6
- data/lib/rspec/mocks/any_instance/message_chains.rb +35 -23
- data/lib/rspec/mocks/any_instance/recorder.rb +74 -49
- data/lib/rspec/mocks/argument_expectation.rb +1 -0
- data/lib/rspec/mocks/argument_matchers.rb +42 -39
- data/lib/rspec/mocks/error_generator.rb +25 -15
- data/lib/rspec/mocks/errors.rb +2 -0
- data/lib/rspec/mocks/{spec_methods.rb → example_methods.rb} +12 -11
- data/lib/rspec/mocks/extensions/instance_exec.rb +3 -0
- data/lib/rspec/mocks/message_expectation.rb +275 -131
- data/lib/rspec/mocks/method_double.rb +32 -7
- data/lib/rspec/mocks/methods.rb +82 -30
- data/lib/rspec/mocks/mock.rb +7 -5
- data/lib/rspec/mocks/order_group.rb +9 -5
- data/lib/rspec/mocks/proxy.rb +33 -12
- data/lib/rspec/mocks/serialization.rb +4 -0
- data/lib/rspec/mocks/space.rb +1 -0
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/{hash_not_including_matcher_spec.rb → hash_excluding_matcher_spec.rb} +2 -2
- data/spec/rspec/mocks/stub_spec.rb +12 -9
- metadata +15 -15
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +0 -33
- data/lib/rspec/mocks/any_instance/stub_chain.rb +0 -35
- data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +0 -34
@@ -1,12 +1,12 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Mocks
|
3
3
|
|
4
|
-
# ArgumentMatchers are
|
4
|
+
# ArgumentMatchers are placeholders that you can include in message
|
5
5
|
# expectations to match arguments against a broader check than simple
|
6
6
|
# equality.
|
7
7
|
#
|
8
|
-
# With the exception of any_args
|
9
|
-
#
|
8
|
+
# With the exception of `any_args` and `no_args`, they all match against
|
9
|
+
# the arg in same position in the argument list.
|
10
10
|
module ArgumentMatchers
|
11
11
|
|
12
12
|
class AnyArgsMatcher
|
@@ -36,8 +36,7 @@ module RSpec
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def ==(value)
|
39
|
-
|
40
|
-
value == @regexp
|
39
|
+
Regexp === value ? value == @regexp : value =~ @regexp
|
41
40
|
end
|
42
41
|
end
|
43
42
|
|
@@ -46,7 +45,7 @@ module RSpec
|
|
46
45
|
end
|
47
46
|
|
48
47
|
def ==(value)
|
49
|
-
|
48
|
+
[true,false].include?(value)
|
50
49
|
end
|
51
50
|
end
|
52
51
|
|
@@ -56,12 +55,9 @@ module RSpec
|
|
56
55
|
end
|
57
56
|
|
58
57
|
def ==(actual)
|
59
|
-
@expected.
|
60
|
-
return false unless actual.has_key?(key) && value == actual[key]
|
61
|
-
end
|
62
|
-
true
|
58
|
+
@expected.all? {|k,v| actual.has_key?(k) && v == actual[k]}
|
63
59
|
rescue NoMethodError
|
64
|
-
|
60
|
+
false
|
65
61
|
end
|
66
62
|
|
67
63
|
def description
|
@@ -69,18 +65,15 @@ module RSpec
|
|
69
65
|
end
|
70
66
|
end
|
71
67
|
|
72
|
-
class
|
68
|
+
class HashExcludingMatcher
|
73
69
|
def initialize(expected)
|
74
70
|
@expected = expected
|
75
71
|
end
|
76
72
|
|
77
73
|
def ==(actual)
|
78
|
-
@expected.
|
79
|
-
return false if actual.has_key?(key) && value == actual[key]
|
80
|
-
end
|
81
|
-
true
|
74
|
+
@expected.none? {|k,v| actual.has_key?(k) && v == actual[k]}
|
82
75
|
rescue NoMethodError
|
83
|
-
|
76
|
+
false
|
84
77
|
end
|
85
78
|
|
86
79
|
def description
|
@@ -94,7 +87,7 @@ module RSpec
|
|
94
87
|
end
|
95
88
|
|
96
89
|
def ==(value)
|
97
|
-
@methods_to_respond_to.all? {
|
90
|
+
@methods_to_respond_to.all? {|sym| value.respond_to?(sym)}
|
98
91
|
end
|
99
92
|
end
|
100
93
|
|
@@ -138,10 +131,11 @@ module RSpec
|
|
138
131
|
end
|
139
132
|
end
|
140
133
|
|
141
|
-
# Passes if object receives
|
142
|
-
# really a more explicit variation of object.should_receive(:message)
|
134
|
+
# Passes if object receives `:message` with any args at all. This is
|
135
|
+
# really a more explicit variation of `object.should_receive(:message)`
|
136
|
+
#
|
137
|
+
# @example
|
143
138
|
#
|
144
|
-
# == Examples
|
145
139
|
# object.should_receive(:message).with(any_args())
|
146
140
|
def any_args
|
147
141
|
AnyArgsMatcher.new
|
@@ -149,7 +143,8 @@ module RSpec
|
|
149
143
|
|
150
144
|
# Passes as long as there is an argument.
|
151
145
|
#
|
152
|
-
#
|
146
|
+
# @example
|
147
|
+
#
|
153
148
|
# object.should_receive(:message).with(anything())
|
154
149
|
def anything
|
155
150
|
AnyArgMatcher.new(nil)
|
@@ -157,7 +152,8 @@ module RSpec
|
|
157
152
|
|
158
153
|
# Passes if no arguments are passed along with the message
|
159
154
|
#
|
160
|
-
#
|
155
|
+
# @example
|
156
|
+
#
|
161
157
|
# object.should_receive(:message).with(no_args)
|
162
158
|
def no_args
|
163
159
|
NoArgsMatcher.new
|
@@ -165,25 +161,28 @@ module RSpec
|
|
165
161
|
|
166
162
|
# Passes if the argument responds to the specified messages.
|
167
163
|
#
|
168
|
-
#
|
164
|
+
# @example
|
165
|
+
#
|
169
166
|
# object.should_receive(:message).with(duck_type(:hello))
|
170
167
|
# object.should_receive(:message).with(duck_type(:hello, :goodbye))
|
171
168
|
def duck_type(*args)
|
172
169
|
DuckTypeMatcher.new(*args)
|
173
170
|
end
|
174
171
|
|
175
|
-
# == Examples
|
176
|
-
# object.should_receive(:message).with(boolean())
|
177
|
-
#
|
178
172
|
# Passes if the argument is boolean.
|
173
|
+
#
|
174
|
+
# @example
|
175
|
+
#
|
176
|
+
# object.should_receive(:message).with(boolean())
|
179
177
|
def boolean
|
180
178
|
BooleanMatcher.new(nil)
|
181
179
|
end
|
182
180
|
|
183
|
-
# Passes if the argument is a hash that includes the specified key(s) or
|
184
|
-
# pairs. If the hash includes other keys, it will still pass.
|
181
|
+
# Passes if the argument is a hash that includes the specified key(s) or
|
182
|
+
# key/value pairs. If the hash includes other keys, it will still pass.
|
183
|
+
#
|
184
|
+
# @example
|
185
185
|
#
|
186
|
-
# == Examples
|
187
186
|
# object.should_receive(:message).with(hash_including(:key => val))
|
188
187
|
# object.should_receive(:message).with(hash_including(:key))
|
189
188
|
# object.should_receive(:message).with(hash_including(:key, :key2 => val2))
|
@@ -191,24 +190,28 @@ module RSpec
|
|
191
190
|
HashIncludingMatcher.new(anythingize_lonely_keys(*args))
|
192
191
|
end
|
193
192
|
|
194
|
-
# Passes if the argument is a hash that doesn't include the specified
|
193
|
+
# Passes if the argument is a hash that doesn't include the specified
|
194
|
+
# key(s) or key/value
|
195
|
+
#
|
196
|
+
# @example
|
195
197
|
#
|
196
|
-
#
|
197
|
-
# object.should_receive(:message).with(
|
198
|
-
# object.should_receive(:message).with(
|
199
|
-
|
200
|
-
|
201
|
-
HashNotIncludingMatcher.new(anythingize_lonely_keys(*args))
|
198
|
+
# object.should_receive(:message).with(hash_excluding(:key => val))
|
199
|
+
# object.should_receive(:message).with(hash_excluding(:key))
|
200
|
+
# object.should_receive(:message).with(hash_excluding(:key, :key2 => :val2))
|
201
|
+
def hash_excluding(*args)
|
202
|
+
HashExcludingMatcher.new(anythingize_lonely_keys(*args))
|
202
203
|
end
|
204
|
+
|
205
|
+
alias_method :hash_not_including, :hash_excluding
|
203
206
|
|
204
|
-
# Passes if arg.instance_of?(klass)
|
207
|
+
# Passes if `arg.instance_of?(klass)`
|
205
208
|
def instance_of(klass)
|
206
209
|
InstanceOf.new(klass)
|
207
210
|
end
|
208
211
|
|
209
212
|
alias_method :an_instance_of, :instance_of
|
210
213
|
|
211
|
-
# Passes if arg.kind_of?(klass)
|
214
|
+
# Passes if `arg.kind_of?(klass)`
|
212
215
|
def kind_of(klass)
|
213
216
|
KindOf.new(klass)
|
214
217
|
end
|
@@ -1,55 +1,65 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Mocks
|
3
|
+
# @private
|
3
4
|
class ErrorGenerator
|
4
5
|
attr_writer :opts
|
5
|
-
|
6
|
+
|
6
7
|
def initialize(target, name, options={})
|
7
8
|
@declared_as = options[:__declared_as] || 'Mock'
|
8
9
|
@target = target
|
9
10
|
@name = name
|
10
11
|
end
|
11
|
-
|
12
|
+
|
13
|
+
# @private
|
12
14
|
def opts
|
13
15
|
@opts ||= {}
|
14
16
|
end
|
15
17
|
|
18
|
+
# @private
|
16
19
|
def raise_unexpected_message_error(sym, *args)
|
17
20
|
__raise "#{intro} received unexpected message :#{sym}#{arg_message(*args)}"
|
18
21
|
end
|
19
|
-
|
22
|
+
|
23
|
+
# @private
|
20
24
|
def raise_unexpected_message_args_error(expectation, *args)
|
21
25
|
expected_args = format_args(*expectation.expected_args)
|
22
26
|
actual_args = format_args(*args)
|
23
27
|
__raise "#{intro} received #{expectation.sym.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
|
24
28
|
end
|
25
|
-
|
29
|
+
|
30
|
+
# @private
|
26
31
|
def raise_similar_message_args_error(expectation, *args)
|
27
32
|
expected_args = format_args(*expectation.expected_args)
|
28
33
|
actual_args = args.collect {|a| format_args(*a)}.join(", ")
|
29
34
|
__raise "#{intro} received #{expectation.sym.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
|
30
35
|
end
|
31
|
-
|
36
|
+
|
37
|
+
# @private
|
32
38
|
def raise_expectation_error(sym, expected_received_count, actual_received_count, *args)
|
33
39
|
__raise "(#{intro}).#{sym}#{format_args(*args)}\n expected: #{count_message(expected_received_count)}\n received: #{count_message(actual_received_count)}"
|
34
40
|
end
|
35
|
-
|
41
|
+
|
42
|
+
# @private
|
36
43
|
def raise_out_of_order_error(sym)
|
37
44
|
__raise "#{intro} received :#{sym} out of order"
|
38
45
|
end
|
39
|
-
|
46
|
+
|
47
|
+
# @private
|
40
48
|
def raise_block_failed_error(sym, detail)
|
41
49
|
__raise "#{intro} received :#{sym} but passed block failed with: #{detail}"
|
42
50
|
end
|
43
|
-
|
51
|
+
|
52
|
+
# @private
|
44
53
|
def raise_missing_block_error(args_to_yield)
|
45
54
|
__raise "#{intro} asked to yield |#{arg_list(*args_to_yield)}| but no block was passed"
|
46
55
|
end
|
47
|
-
|
56
|
+
|
57
|
+
# @private
|
48
58
|
def raise_wrong_arity_error(args_to_yield, arity)
|
49
59
|
__raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with arity of #{arity}"
|
50
60
|
end
|
51
|
-
|
52
|
-
|
61
|
+
|
62
|
+
private
|
53
63
|
|
54
64
|
def intro
|
55
65
|
if @name
|
@@ -64,16 +74,16 @@ module RSpec
|
|
64
74
|
"nil"
|
65
75
|
end
|
66
76
|
end
|
67
|
-
|
77
|
+
|
68
78
|
def __raise(message)
|
69
79
|
message = opts[:message] unless opts[:message].nil?
|
70
80
|
Kernel::raise(RSpec::Mocks::MockExpectationError, message)
|
71
81
|
end
|
72
|
-
|
82
|
+
|
73
83
|
def arg_message(*args)
|
74
84
|
" with " + format_args(*args)
|
75
85
|
end
|
76
|
-
|
86
|
+
|
77
87
|
def format_args(*args)
|
78
88
|
args.empty? ? "(no args)" : "(" + arg_list(*args) + ")"
|
79
89
|
end
|
@@ -81,7 +91,7 @@ module RSpec
|
|
81
91
|
def arg_list(*args)
|
82
92
|
args.collect {|arg| arg.respond_to?(:description) ? arg.description : arg.inspect}.join(", ")
|
83
93
|
end
|
84
|
-
|
94
|
+
|
85
95
|
def count_message(count)
|
86
96
|
return "at least #{pretty_print(count.abs)}" if count < 0
|
87
97
|
return pretty_print(count)
|
data/lib/rspec/mocks/errors.rb
CHANGED
@@ -5,37 +5,38 @@ module RSpec
|
|
5
5
|
|
6
6
|
# Creates an instance of RSpec::Mocks::Mock.
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# `name` is used for failure reporting, so you should use the role that
|
9
9
|
# the mock is playing in the example.
|
10
10
|
#
|
11
|
-
# Use
|
11
|
+
# Use `stubs` to declare one or more method stubs in one statement.
|
12
12
|
#
|
13
|
-
#
|
13
|
+
# @example
|
14
14
|
#
|
15
15
|
# book = double("book", :title => "The RSpec Book")
|
16
|
-
# book.title
|
16
|
+
# book.title #=> "The RSpec Book"
|
17
17
|
#
|
18
|
-
# card = double("card", :suit => "Spades", :rank => "A"
|
19
|
-
# card.suit
|
20
|
-
# card.rank
|
18
|
+
# card = double("card", :suit => "Spades", :rank => "A")
|
19
|
+
# card.suit #=> "Spades"
|
20
|
+
# card.rank #=> "A"
|
21
21
|
def double(*args)
|
22
22
|
declare_double('Double', *args)
|
23
23
|
end
|
24
24
|
|
25
|
-
# Just like double
|
25
|
+
# Just like double
|
26
26
|
def mock(*args)
|
27
27
|
declare_double('Mock', *args)
|
28
28
|
end
|
29
29
|
|
30
|
-
# Just like double
|
30
|
+
# Just like double
|
31
31
|
def stub(*args)
|
32
32
|
declare_double('Stub', *args)
|
33
33
|
end
|
34
34
|
|
35
35
|
# Disables warning messages about expectations being set on nil.
|
36
36
|
#
|
37
|
-
# By default warning messages are issued when expectations are set on
|
38
|
-
# prevent false-positives and to catch potential bugs
|
37
|
+
# By default warning messages are issued when expectations are set on
|
38
|
+
# nil. This is to prevent false-positives and to catch potential bugs
|
39
|
+
# early on.
|
39
40
|
def allow_message_expectations_on_nil
|
40
41
|
Proxy.allow_message_expectations_on_nil
|
41
42
|
end
|
@@ -1,7 +1,10 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Mocks
|
3
|
+
# @private
|
3
4
|
module InstanceExec
|
4
5
|
unless respond_to?(:instance_exec)
|
6
|
+
# @private
|
7
|
+
#
|
5
8
|
# based on Bounded Spec InstanceExec (Mauricio Fernandez)
|
6
9
|
# http://eigenclass.org/hiki/bounded+space+instance_exec
|
7
10
|
# - uses singleton_class of matcher instead of global
|
@@ -1,13 +1,15 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Mocks
|
3
3
|
|
4
|
-
class
|
4
|
+
class MessageExpectation
|
5
|
+
# @private
|
5
6
|
attr_reader :sym
|
6
7
|
attr_writer :expected_received_count, :method_block, :expected_from
|
7
8
|
protected :expected_received_count=, :method_block=, :expected_from=
|
8
9
|
attr_accessor :error_generator
|
9
10
|
protected :error_generator, :error_generator=
|
10
|
-
|
11
|
+
|
12
|
+
# @private
|
11
13
|
def initialize(error_generator, expectation_ordering, expected_from, sym, method_block, expected_received_count=1, opts={}, &implementation)
|
12
14
|
@error_generator = error_generator
|
13
15
|
@error_generator.opts = opts
|
@@ -20,7 +22,7 @@ module RSpec
|
|
20
22
|
@args_expectation = ArgumentExpectation.new(ArgumentMatchers::AnyArgsMatcher.new)
|
21
23
|
@consecutive = false
|
22
24
|
@exception_to_raise = nil
|
23
|
-
@
|
25
|
+
@args_to_throw = []
|
24
26
|
@order_group = expectation_ordering
|
25
27
|
@at_least = nil
|
26
28
|
@at_most = nil
|
@@ -31,7 +33,8 @@ module RSpec
|
|
31
33
|
@return_block = implementation
|
32
34
|
@eval_context = nil
|
33
35
|
end
|
34
|
-
|
36
|
+
|
37
|
+
# @private
|
35
38
|
def build_child(expected_from, method_block, expected_received_count, opts={})
|
36
39
|
child = clone
|
37
40
|
child.expected_from = expected_from
|
@@ -44,80 +47,140 @@ module RSpec
|
|
44
47
|
child.clone_args_to_yield(*@args_to_yield)
|
45
48
|
child
|
46
49
|
end
|
47
|
-
|
50
|
+
|
51
|
+
# @private
|
48
52
|
def expected_args
|
49
53
|
@args_expectation.args
|
50
54
|
end
|
51
55
|
|
56
|
+
# @overload and_return(value)
|
57
|
+
# @overload and_return(first_value, second_value)
|
58
|
+
# @overload and_return(&block)
|
59
|
+
#
|
60
|
+
# Tells the object to return a value when it receives the message. Given
|
61
|
+
# more than one value, the first value is returned the first time the
|
62
|
+
# message is received, the second value is returned the next time, etc,
|
63
|
+
# etc.
|
64
|
+
#
|
65
|
+
# If the message is received more times than there are values, the last
|
66
|
+
# value is received for every subsequent call.
|
67
|
+
#
|
68
|
+
# The block format is still supported, but is unofficially deprecated in
|
69
|
+
# favor of just passing a block to the stub method.
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
#
|
73
|
+
# counter.stub(:count).and_return(1)
|
74
|
+
# counter.count # => 1
|
75
|
+
# counter.count # => 1
|
76
|
+
#
|
77
|
+
# counter.stub(:count).and_return(1,2,3)
|
78
|
+
# counter.count # => 1
|
79
|
+
# counter.count # => 2
|
80
|
+
# counter.count # => 3
|
81
|
+
# counter.count # => 3
|
82
|
+
# counter.count # => 3
|
83
|
+
# # etc
|
84
|
+
#
|
85
|
+
# # Supported, but ...
|
86
|
+
# counter.stub(:count).and_return { 1 }
|
87
|
+
# counter.count # => 1
|
88
|
+
#
|
89
|
+
# # ... this is prefered
|
90
|
+
# counter.stub(:count) { 1 }
|
91
|
+
# counter.count # => 1
|
52
92
|
def and_return(*values, &return_block)
|
53
93
|
Kernel::raise AmbiguousReturnError unless @method_block.nil?
|
54
94
|
case values.size
|
55
|
-
|
56
|
-
|
95
|
+
when 0 then value = nil
|
96
|
+
when 1 then value = values[0]
|
57
97
|
else
|
58
98
|
value = values
|
59
99
|
@consecutive = true
|
60
100
|
@expected_received_count = values.size if !ignoring_args? &&
|
61
|
-
|
101
|
+
@expected_received_count < values.size
|
62
102
|
end
|
63
103
|
@return_block = block_given? ? return_block : lambda { value }
|
64
104
|
end
|
65
|
-
|
66
|
-
#
|
67
|
-
#
|
105
|
+
|
106
|
+
# @overload and_raise
|
107
|
+
# @overload and_raise(ExceptionClass)
|
108
|
+
# @overload and_raise(exception_instance)
|
109
|
+
#
|
110
|
+
# Tells the object to raise an exception when the message is received.
|
68
111
|
#
|
69
|
-
#
|
112
|
+
# @note
|
70
113
|
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
114
|
+
# When you pass an exception class, the MessageExpectation will raise
|
115
|
+
# an instance of it, creating it with `new`. If the exception class
|
116
|
+
# initializer requires any parameters, you must pass in an instance and
|
117
|
+
# not the class.
|
75
118
|
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
# and_raise(
|
119
|
+
# @example
|
120
|
+
#
|
121
|
+
# car.stub(:go).and_raise
|
122
|
+
# car.stub(:go).and_raise(OutOfGas)
|
123
|
+
# car.stub(:go).and_raise(OutOfGas.new(2, :oz))
|
80
124
|
def and_raise(exception=Exception)
|
81
125
|
@exception_to_raise = exception
|
82
126
|
end
|
83
|
-
|
84
|
-
|
85
|
-
|
127
|
+
|
128
|
+
# @overload and_throw(symbol)
|
129
|
+
# @overload and_throw(symbol, object)
|
130
|
+
#
|
131
|
+
# Tells the object to throw a symbol (with the object if that form is
|
132
|
+
# used) when the message is received.
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
#
|
136
|
+
# car.stub(:go).and_throw(:out_of_gas)
|
137
|
+
# car.stub(:go).and_throw(:out_of_gas, :level => 0.1)
|
138
|
+
def and_throw(symbol, object = nil)
|
139
|
+
@args_to_throw << symbol
|
140
|
+
@args_to_throw << object if object
|
86
141
|
end
|
87
|
-
|
142
|
+
|
143
|
+
# Tells the object to yield one or more args to a block when the message
|
144
|
+
# is received.
|
145
|
+
#
|
146
|
+
# @example
|
147
|
+
#
|
148
|
+
# stream.stub(:open).and_yield(StringIO.new)
|
88
149
|
def and_yield(*args, &block)
|
89
150
|
if @args_to_yield_were_cloned
|
90
151
|
@args_to_yield.clear
|
91
152
|
@args_to_yield_were_cloned = false
|
92
153
|
end
|
93
|
-
|
154
|
+
|
94
155
|
if block
|
95
156
|
@eval_context = Object.new
|
96
157
|
@eval_context.extend RSpec::Mocks::InstanceExec
|
97
158
|
yield @eval_context
|
98
159
|
end
|
99
|
-
|
160
|
+
|
100
161
|
@args_to_yield << args
|
101
162
|
self
|
102
163
|
end
|
103
|
-
|
164
|
+
|
165
|
+
# @private
|
104
166
|
def matches?(sym, *args)
|
105
167
|
@sym == sym and @args_expectation.args_match?(*args)
|
106
168
|
end
|
107
|
-
|
169
|
+
|
170
|
+
# @private
|
108
171
|
def invoke(*args, &block)
|
109
172
|
if @expected_received_count == 0
|
110
173
|
@failed_fast = true
|
111
174
|
@actual_received_count += 1
|
112
175
|
@error_generator.raise_expectation_error(@sym, @expected_received_count, @actual_received_count, *args)
|
113
176
|
end
|
114
|
-
|
177
|
+
|
115
178
|
@order_group.handle_order_constraint self
|
116
179
|
|
117
180
|
begin
|
118
|
-
Kernel::raise
|
119
|
-
Kernel::throw
|
120
|
-
|
181
|
+
Kernel::raise(@exception_to_raise) unless @exception_to_raise.nil?
|
182
|
+
Kernel::throw(*@args_to_throw) unless @args_to_throw.empty?
|
183
|
+
|
121
184
|
default_return_val = if !@method_block.nil?
|
122
185
|
invoke_method_block(*args, &block)
|
123
186
|
elsif !@args_to_yield.empty? || @eval_context
|
@@ -125,7 +188,7 @@ module RSpec
|
|
125
188
|
else
|
126
189
|
nil
|
127
190
|
end
|
128
|
-
|
191
|
+
|
129
192
|
if @consecutive
|
130
193
|
invoke_consecutive_return_block(*args, &block)
|
131
194
|
elsif @return_block
|
@@ -138,111 +201,61 @@ module RSpec
|
|
138
201
|
end
|
139
202
|
end
|
140
203
|
|
204
|
+
# @private
|
141
205
|
def called_max_times?
|
142
206
|
@expected_received_count != :any && @expected_received_count > 0 &&
|
143
207
|
@actual_received_count >= @expected_received_count
|
144
208
|
end
|
145
|
-
|
146
|
-
protected
|
147
|
-
|
148
|
-
def invoke_method_block(*args, &block)
|
149
|
-
begin
|
150
|
-
@method_block.call(*args, &block)
|
151
|
-
rescue => detail
|
152
|
-
@error_generator.raise_block_failed_error(@sym, detail.message)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def invoke_with_yield(&block)
|
157
|
-
if block.nil?
|
158
|
-
@error_generator.raise_missing_block_error @args_to_yield
|
159
|
-
end
|
160
|
-
value = nil
|
161
|
-
@args_to_yield.each do |args_to_yield_this_time|
|
162
|
-
if block.arity > -1 && args_to_yield_this_time.length != block.arity
|
163
|
-
@error_generator.raise_wrong_arity_error args_to_yield_this_time, block.arity
|
164
|
-
end
|
165
|
-
value = eval_block(*args_to_yield_this_time, &block)
|
166
|
-
end
|
167
|
-
value
|
168
|
-
end
|
169
|
-
|
170
|
-
def eval_block(*args, &block)
|
171
|
-
if @eval_context
|
172
|
-
@eval_context.instance_exec(*args, &block)
|
173
|
-
else
|
174
|
-
block.call(*args)
|
175
|
-
end
|
176
|
-
end
|
177
209
|
|
178
|
-
|
179
|
-
value = invoke_return_block(*args, &block)
|
180
|
-
index = [@actual_received_count, value.size-1].min
|
181
|
-
value[index]
|
182
|
-
end
|
183
|
-
|
184
|
-
def invoke_return_block(*args, &block)
|
185
|
-
args << block unless block.nil?
|
186
|
-
# Ruby 1.9 - when we set @return_block to return values
|
187
|
-
# regardless of arguments, any arguments will result in
|
188
|
-
# a "wrong number of arguments" error
|
189
|
-
@return_block.arity == 0 ? @return_block.call : @return_block.call(*args)
|
190
|
-
end
|
191
|
-
|
192
|
-
def clone_args_to_yield(*args)
|
193
|
-
@args_to_yield = args.clone
|
194
|
-
@args_to_yield_were_cloned = true
|
195
|
-
end
|
196
|
-
|
197
|
-
def failed_fast?
|
198
|
-
@failed_fast
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
class MessageExpectation < BaseExpectation
|
203
|
-
|
210
|
+
# @private
|
204
211
|
def matches_name_but_not_args(sym, *args)
|
205
212
|
@sym == sym and not @args_expectation.args_match?(*args)
|
206
213
|
end
|
207
|
-
|
214
|
+
|
215
|
+
# @private
|
208
216
|
def verify_messages_received
|
209
|
-
|
210
|
-
|
211
|
-
generate_error
|
217
|
+
generate_error unless expected_messages_received? || failed_fast?
|
212
218
|
rescue RSpec::Mocks::MockExpectationError => error
|
213
219
|
error.backtrace.insert(0, @expected_from)
|
214
220
|
Kernel::raise error
|
215
221
|
end
|
216
|
-
|
222
|
+
|
223
|
+
# @private
|
217
224
|
def expected_messages_received?
|
218
|
-
ignoring_args? || matches_exact_count? ||
|
219
|
-
matches_at_least_count? || matches_at_most_count?
|
225
|
+
ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
|
220
226
|
end
|
221
|
-
|
227
|
+
|
228
|
+
# @private
|
222
229
|
def ignoring_args?
|
223
230
|
@expected_received_count == :any
|
224
231
|
end
|
225
|
-
|
232
|
+
|
233
|
+
# @private
|
226
234
|
def matches_at_least_count?
|
227
235
|
@at_least && @actual_received_count >= @expected_received_count
|
228
236
|
end
|
229
|
-
|
237
|
+
|
238
|
+
# @private
|
230
239
|
def matches_at_most_count?
|
231
240
|
@at_most && @actual_received_count <= @expected_received_count
|
232
241
|
end
|
233
|
-
|
242
|
+
|
243
|
+
# @private
|
234
244
|
def matches_exact_count?
|
235
245
|
@expected_received_count == @actual_received_count
|
236
246
|
end
|
237
|
-
|
247
|
+
|
248
|
+
# @private
|
238
249
|
def similar_messages
|
239
250
|
@similar_messages ||= []
|
240
251
|
end
|
241
252
|
|
253
|
+
# @private
|
242
254
|
def advise(*args)
|
243
255
|
similar_messages << args
|
244
256
|
end
|
245
|
-
|
257
|
+
|
258
|
+
# @private
|
246
259
|
def generate_error
|
247
260
|
if similar_messages.empty?
|
248
261
|
@error_generator.raise_expectation_error(@sym, @expected_received_count, @actual_received_count, *@args_expectation.args)
|
@@ -251,107 +264,238 @@ module RSpec
|
|
251
264
|
end
|
252
265
|
end
|
253
266
|
|
267
|
+
# Constrains a stub or message expectation to invocations with specific
|
268
|
+
# arguments.
|
269
|
+
#
|
270
|
+
# With a stub, if the message might be received with other args as well,
|
271
|
+
# you should stub a default value first, and then stub or mock the same
|
272
|
+
# message using `with` to constrain to specific arguments.
|
273
|
+
#
|
274
|
+
# A message expectation will fail if the message is received with different
|
275
|
+
# arguments.
|
276
|
+
#
|
277
|
+
# @example
|
278
|
+
#
|
279
|
+
# cart.stub(:add) { :failure }
|
280
|
+
# cart.stub(:add).with(Book.new(:isbn => 1934356379)) { :success }
|
281
|
+
# cart.add(Book.new(:isbn => 1234567890))
|
282
|
+
# # => :failure
|
283
|
+
# cart.add(Book.new(:isbn => 1934356379))
|
284
|
+
# # => :success
|
285
|
+
#
|
286
|
+
# cart.should_receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
|
287
|
+
# cart.add(Book.new(:isbn => 1234567890))
|
288
|
+
# # => failed expectation
|
289
|
+
# cart.add(Book.new(:isbn => 1934356379))
|
290
|
+
# # => passes
|
254
291
|
def with(*args, &block)
|
255
292
|
@return_block = block if block_given? unless args.empty?
|
256
293
|
@args_expectation = ArgumentExpectation.new(*args, &block)
|
257
294
|
self
|
258
295
|
end
|
259
|
-
|
296
|
+
|
297
|
+
# Constrain a message expectation to be received a specific number of
|
298
|
+
# times.
|
299
|
+
#
|
300
|
+
# @example
|
301
|
+
#
|
302
|
+
# dealer.should_recieve(:deal_card).exactly(10).times
|
260
303
|
def exactly(n, &block)
|
261
304
|
@method_block = block if block
|
262
305
|
set_expected_received_count :exactly, n
|
263
306
|
self
|
264
307
|
end
|
265
|
-
|
308
|
+
|
309
|
+
# Constrain a message expectation to be received at least a specific
|
310
|
+
# number of times.
|
311
|
+
#
|
312
|
+
# @example
|
313
|
+
#
|
314
|
+
# dealer.should_recieve(:deal_card).at_least(9).times
|
266
315
|
def at_least(n, &block)
|
267
316
|
@method_block = block if block
|
268
317
|
set_expected_received_count :at_least, n
|
269
318
|
self
|
270
319
|
end
|
271
|
-
|
320
|
+
|
321
|
+
# Constrain a message expectation to be received at most a specific
|
322
|
+
# number of times.
|
323
|
+
#
|
324
|
+
# @example
|
325
|
+
#
|
326
|
+
# dealer.should_recieve(:deal_card).at_most(10).times
|
272
327
|
def at_most(n, &block)
|
273
328
|
@method_block = block if block
|
274
329
|
set_expected_received_count :at_most, n
|
275
330
|
self
|
276
331
|
end
|
277
332
|
|
333
|
+
# Syntactic sugar for `exactly`, `at_least` and `at_most`
|
334
|
+
#
|
335
|
+
# @example
|
336
|
+
#
|
337
|
+
# dealer.should_recieve(:deal_card).exactly(10).times
|
338
|
+
# dealer.should_recieve(:deal_card).at_least(10).times
|
339
|
+
# dealer.should_recieve(:deal_card).at_most(10).times
|
278
340
|
def times(&block)
|
279
341
|
@method_block = block if block
|
280
342
|
self
|
281
343
|
end
|
282
|
-
|
344
|
+
|
345
|
+
|
346
|
+
# Allows an expected message to be received any number of times.
|
283
347
|
def any_number_of_times(&block)
|
284
348
|
@method_block = block if block
|
285
349
|
@expected_received_count = :any
|
286
350
|
self
|
287
351
|
end
|
288
|
-
|
352
|
+
|
353
|
+
# Expect a message not to be received at all.
|
354
|
+
#
|
355
|
+
# @example
|
356
|
+
#
|
357
|
+
# car.should_receive(:stop).never
|
289
358
|
def never
|
290
359
|
@expected_received_count = 0
|
291
360
|
self
|
292
361
|
end
|
293
|
-
|
362
|
+
|
363
|
+
# Expect a message to be received exactly one time.
|
364
|
+
#
|
365
|
+
# @example
|
366
|
+
#
|
367
|
+
# car.should_receive(:go).once
|
294
368
|
def once(&block)
|
295
369
|
@method_block = block if block
|
296
370
|
set_expected_received_count :exactly, 1
|
297
371
|
self
|
298
372
|
end
|
299
|
-
|
373
|
+
|
374
|
+
# Expect a message to be received exactly two times.
|
375
|
+
#
|
376
|
+
# @example
|
377
|
+
#
|
378
|
+
# car.should_receive(:go).twice
|
300
379
|
def twice(&block)
|
301
380
|
@method_block = block if block
|
302
381
|
set_expected_received_count :exactly, 2
|
303
382
|
self
|
304
383
|
end
|
305
|
-
|
384
|
+
|
385
|
+
# Expect messages to be received in a specific order.
|
386
|
+
#
|
387
|
+
# @example
|
388
|
+
#
|
389
|
+
# api.should_receive(:prepare).ordered
|
390
|
+
# api.should_receive(:run).ordered
|
391
|
+
# api.should_receive(:finish).ordered
|
306
392
|
def ordered(&block)
|
307
393
|
@method_block = block if block
|
308
394
|
@order_group.register(self)
|
309
395
|
@ordered = true
|
310
396
|
self
|
311
397
|
end
|
312
|
-
|
398
|
+
|
399
|
+
# @private
|
313
400
|
def negative_expectation_for?(sym)
|
314
401
|
return false
|
315
402
|
end
|
316
|
-
|
403
|
+
|
404
|
+
# @private
|
317
405
|
def actual_received_count_matters?
|
318
406
|
@at_least || @at_most || @exactly
|
319
407
|
end
|
320
408
|
|
409
|
+
# @private
|
321
410
|
def increase_actual_received_count!
|
322
411
|
@actual_received_count += 1
|
323
412
|
end
|
324
413
|
|
325
414
|
protected
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
@
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
415
|
+
|
416
|
+
def invoke_method_block(*args, &block)
|
417
|
+
begin
|
418
|
+
@method_block.call(*args, &block)
|
419
|
+
rescue => detail
|
420
|
+
@error_generator.raise_block_failed_error(@sym, detail.message)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
def invoke_with_yield(&block)
|
425
|
+
if block.nil?
|
426
|
+
@error_generator.raise_missing_block_error @args_to_yield
|
427
|
+
end
|
428
|
+
value = nil
|
429
|
+
@args_to_yield.each do |args_to_yield_this_time|
|
430
|
+
if block.arity > -1 && args_to_yield_this_time.length != block.arity
|
431
|
+
@error_generator.raise_wrong_arity_error args_to_yield_this_time, block.arity
|
337
432
|
end
|
433
|
+
value = eval_block(*args_to_yield_this_time, &block)
|
338
434
|
end
|
339
|
-
|
340
|
-
|
341
|
-
|
435
|
+
value
|
436
|
+
end
|
437
|
+
|
438
|
+
def eval_block(*args, &block)
|
439
|
+
if @eval_context
|
440
|
+
@eval_context.instance_exec(*args, &block)
|
441
|
+
else
|
442
|
+
block.call(*args)
|
342
443
|
end
|
444
|
+
end
|
445
|
+
|
446
|
+
def invoke_consecutive_return_block(*args, &block)
|
447
|
+
value = invoke_return_block(*args, &block)
|
448
|
+
index = [@actual_received_count, value.size-1].min
|
449
|
+
value[index]
|
450
|
+
end
|
451
|
+
|
452
|
+
def invoke_return_block(*args, &block)
|
453
|
+
args << block unless block.nil?
|
454
|
+
# Ruby 1.9 - when we set @return_block to return values
|
455
|
+
# regardless of arguments, any arguments will result in
|
456
|
+
# a "wrong number of arguments" error
|
457
|
+
@return_block.arity == 0 ? @return_block.call : @return_block.call(*args)
|
458
|
+
end
|
459
|
+
|
460
|
+
def clone_args_to_yield(*args)
|
461
|
+
@args_to_yield = args.clone
|
462
|
+
@args_to_yield_were_cloned = true
|
463
|
+
end
|
464
|
+
|
465
|
+
def failed_fast?
|
466
|
+
@failed_fast
|
467
|
+
end
|
343
468
|
|
469
|
+
def set_expected_received_count(relativity, n)
|
470
|
+
@at_least = (relativity == :at_least)
|
471
|
+
@at_most = (relativity == :at_most)
|
472
|
+
@exactly = (relativity == :exactly)
|
473
|
+
@expected_received_count = case n
|
474
|
+
when Numeric
|
475
|
+
n
|
476
|
+
when :once
|
477
|
+
1
|
478
|
+
when :twice
|
479
|
+
2
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def clear_actual_received_count!
|
484
|
+
@actual_received_count = 0
|
485
|
+
end
|
344
486
|
end
|
345
|
-
|
487
|
+
|
488
|
+
# @private
|
346
489
|
class NegativeMessageExpectation < MessageExpectation
|
490
|
+
# @private
|
347
491
|
def initialize(message, expectation_ordering, expected_from, sym, method_block)
|
348
492
|
super(message, expectation_ordering, expected_from, sym, method_block, 0)
|
349
493
|
end
|
350
|
-
|
494
|
+
|
495
|
+
# @private
|
351
496
|
def negative_expectation_for?(sym)
|
352
497
|
return @sym == sym
|
353
498
|
end
|
354
499
|
end
|
355
|
-
|
356
500
|
end
|
357
501
|
end
|