rspec-mocks 2.8.0.rc1 → 2.8.0.rc2
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/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
|