rspec-mocks 2.10.0 → 2.11.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/Changelog.md +36 -1
- data/README.md +14 -2
- data/features/stubbing_constants/README.md +62 -0
- data/features/stubbing_constants/stub_defined_constant.feature +79 -0
- data/features/stubbing_constants/stub_undefined_constant.feature +50 -0
- data/lib/rspec/mocks/any_instance/chain.rb +0 -81
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +57 -0
- data/lib/rspec/mocks/any_instance/recorder.rb +6 -1
- data/lib/rspec/mocks/any_instance/stub_chain.rb +37 -0
- data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +25 -0
- data/lib/rspec/mocks/any_instance.rb +37 -1
- data/lib/rspec/mocks/argument_list_matcher.rb +93 -0
- data/lib/rspec/mocks/argument_matchers.rb +39 -31
- data/lib/rspec/mocks/error_generator.rb +7 -0
- data/lib/rspec/mocks/example_methods.rb +41 -0
- data/lib/rspec/mocks/framework.rb +2 -1
- data/lib/rspec/mocks/message_expectation.rb +27 -15
- data/lib/rspec/mocks/methods.rb +4 -0
- data/lib/rspec/mocks/proxy.rb +10 -4
- data/lib/rspec/mocks/stub_const.rb +280 -0
- data/lib/rspec/mocks/test_double.rb +3 -2
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/any_instance/message_chains_spec.rb +1 -1
- data/spec/rspec/mocks/any_instance_spec.rb +66 -26
- data/spec/rspec/mocks/argument_expectation_spec.rb +7 -7
- data/spec/rspec/mocks/at_least_spec.rb +89 -50
- data/spec/rspec/mocks/block_return_value_spec.rb +8 -0
- data/spec/rspec/mocks/mock_spec.rb +261 -246
- data/spec/rspec/mocks/multiple_return_value_spec.rb +2 -2
- data/spec/rspec/mocks/null_object_mock_spec.rb +22 -0
- data/spec/rspec/mocks/stub_chain_spec.rb +47 -47
- data/spec/rspec/mocks/stub_const_spec.rb +309 -0
- data/spec/rspec/mocks/stub_spec.rb +2 -2
- data/spec/spec_helper.rb +3 -41
- metadata +18 -6
- data/lib/rspec/mocks/argument_expectation.rb +0 -52
|
@@ -7,6 +7,8 @@ module RSpec
|
|
|
7
7
|
#
|
|
8
8
|
# With the exception of `any_args` and `no_args`, they all match against
|
|
9
9
|
# the arg in same position in the argument list.
|
|
10
|
+
#
|
|
11
|
+
# @see ArgumentListMatcher
|
|
10
12
|
module ArgumentMatchers
|
|
11
13
|
|
|
12
14
|
class AnyArgsMatcher
|
|
@@ -64,7 +66,7 @@ module RSpec
|
|
|
64
66
|
"hash_including(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})"
|
|
65
67
|
end
|
|
66
68
|
end
|
|
67
|
-
|
|
69
|
+
|
|
68
70
|
class HashExcludingMatcher
|
|
69
71
|
def initialize(expected)
|
|
70
72
|
@expected = expected
|
|
@@ -80,7 +82,7 @@ module RSpec
|
|
|
80
82
|
"hash_not_including(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})"
|
|
81
83
|
end
|
|
82
84
|
end
|
|
83
|
-
|
|
85
|
+
|
|
84
86
|
class DuckTypeMatcher
|
|
85
87
|
def initialize(*methods_to_respond_to)
|
|
86
88
|
@methods_to_respond_to = methods_to_respond_to
|
|
@@ -110,47 +112,47 @@ module RSpec
|
|
|
110
112
|
@given == expected
|
|
111
113
|
end
|
|
112
114
|
end
|
|
113
|
-
|
|
115
|
+
|
|
114
116
|
class InstanceOf
|
|
115
117
|
def initialize(klass)
|
|
116
118
|
@klass = klass
|
|
117
119
|
end
|
|
118
|
-
|
|
120
|
+
|
|
119
121
|
def ==(actual)
|
|
120
122
|
actual.instance_of?(@klass)
|
|
121
123
|
end
|
|
122
124
|
end
|
|
123
|
-
|
|
125
|
+
|
|
124
126
|
class KindOf
|
|
125
127
|
def initialize(klass)
|
|
126
128
|
@klass = klass
|
|
127
129
|
end
|
|
128
|
-
|
|
130
|
+
|
|
129
131
|
def ==(actual)
|
|
130
132
|
actual.kind_of?(@klass)
|
|
131
133
|
end
|
|
132
134
|
end
|
|
133
135
|
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
+
# Matches any args at all. Supports a more explicit variation of
|
|
137
|
+
# `object.should_receive(:message)`
|
|
136
138
|
#
|
|
137
139
|
# @example
|
|
138
140
|
#
|
|
139
|
-
# object.should_receive(:message).with(any_args
|
|
141
|
+
# object.should_receive(:message).with(any_args)
|
|
140
142
|
def any_args
|
|
141
143
|
AnyArgsMatcher.new
|
|
142
144
|
end
|
|
143
|
-
|
|
144
|
-
#
|
|
145
|
+
|
|
146
|
+
# Matches any argument at all.
|
|
145
147
|
#
|
|
146
148
|
# @example
|
|
147
149
|
#
|
|
148
|
-
# object.should_receive(:message).with(anything
|
|
150
|
+
# object.should_receive(:message).with(anything)
|
|
149
151
|
def anything
|
|
150
152
|
AnyArgMatcher.new(nil)
|
|
151
153
|
end
|
|
152
|
-
|
|
153
|
-
#
|
|
154
|
+
|
|
155
|
+
# Matches no arguments.
|
|
154
156
|
#
|
|
155
157
|
# @example
|
|
156
158
|
#
|
|
@@ -158,8 +160,8 @@ module RSpec
|
|
|
158
160
|
def no_args
|
|
159
161
|
NoArgsMatcher.new
|
|
160
162
|
end
|
|
161
|
-
|
|
162
|
-
#
|
|
163
|
+
|
|
164
|
+
# Matches if the actual argument responds to the specified messages.
|
|
163
165
|
#
|
|
164
166
|
# @example
|
|
165
167
|
#
|
|
@@ -169,7 +171,7 @@ module RSpec
|
|
|
169
171
|
DuckTypeMatcher.new(*args)
|
|
170
172
|
end
|
|
171
173
|
|
|
172
|
-
#
|
|
174
|
+
# Matches a boolean value.
|
|
173
175
|
#
|
|
174
176
|
# @example
|
|
175
177
|
#
|
|
@@ -177,9 +179,9 @@ module RSpec
|
|
|
177
179
|
def boolean
|
|
178
180
|
BooleanMatcher.new(nil)
|
|
179
181
|
end
|
|
180
|
-
|
|
181
|
-
#
|
|
182
|
-
#
|
|
182
|
+
|
|
183
|
+
# Matches a hash that includes the specified key(s) or key/value pairs.
|
|
184
|
+
# Ignores any additional keys.
|
|
183
185
|
#
|
|
184
186
|
# @example
|
|
185
187
|
#
|
|
@@ -189,9 +191,8 @@ module RSpec
|
|
|
189
191
|
def hash_including(*args)
|
|
190
192
|
HashIncludingMatcher.new(anythingize_lonely_keys(*args))
|
|
191
193
|
end
|
|
192
|
-
|
|
193
|
-
#
|
|
194
|
-
# key(s) or key/value
|
|
194
|
+
|
|
195
|
+
# Matches a hash that doesn't include the specified key(s) or key/value.
|
|
195
196
|
#
|
|
196
197
|
# @example
|
|
197
198
|
#
|
|
@@ -203,23 +204,30 @@ module RSpec
|
|
|
203
204
|
end
|
|
204
205
|
|
|
205
206
|
alias_method :hash_not_including, :hash_excluding
|
|
206
|
-
|
|
207
|
-
#
|
|
207
|
+
|
|
208
|
+
# Matches if `arg.instance_of?(klass)`
|
|
209
|
+
#
|
|
210
|
+
# @example
|
|
211
|
+
#
|
|
212
|
+
# object.should_receive(:message).with(instance_of(Thing))
|
|
208
213
|
def instance_of(klass)
|
|
209
214
|
InstanceOf.new(klass)
|
|
210
215
|
end
|
|
211
|
-
|
|
216
|
+
|
|
212
217
|
alias_method :an_instance_of, :instance_of
|
|
213
|
-
|
|
214
|
-
#
|
|
218
|
+
|
|
219
|
+
# Matches if `arg.kind_of?(klass)`
|
|
220
|
+
# @example
|
|
221
|
+
#
|
|
222
|
+
# object.should_receive(:message).with(kind_of(Thing))
|
|
215
223
|
def kind_of(klass)
|
|
216
224
|
KindOf.new(klass)
|
|
217
225
|
end
|
|
218
|
-
|
|
226
|
+
|
|
219
227
|
alias_method :a_kind_of, :kind_of
|
|
220
|
-
|
|
228
|
+
|
|
221
229
|
private
|
|
222
|
-
|
|
230
|
+
|
|
223
231
|
def anythingize_lonely_keys(*args)
|
|
224
232
|
hash = args.last.class == Hash ? args.delete_at(-1) : {}
|
|
225
233
|
args.each { | arg | hash[arg] = anything }
|
|
@@ -27,6 +27,13 @@ module RSpec
|
|
|
27
27
|
__raise "#{intro} received #{expectation.message.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
# @private
|
|
31
|
+
def raise_missing_default_stub_error(expectation,*args)
|
|
32
|
+
expected_args = format_args(*expectation.expected_args)
|
|
33
|
+
actual_args = args.collect {|a| format_args(*a)}.join(", ")
|
|
34
|
+
__raise "#{intro} received #{expectation.message.inspect} with unexpected arguments\n Please stub a default value first if message might be received with other args as well. \n"
|
|
35
|
+
end
|
|
36
|
+
|
|
30
37
|
# @private
|
|
31
38
|
def raise_similar_message_args_error(expectation, *args)
|
|
32
39
|
expected_args = format_args(*expectation.expected_args)
|
|
@@ -41,6 +41,47 @@ module RSpec
|
|
|
41
41
|
Proxy.allow_message_expectations_on_nil
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
# Stubs the named constant with the given value.
|
|
45
|
+
# Like method stubs, the constant will be restored
|
|
46
|
+
# to its original value (or lack of one, if it was
|
|
47
|
+
# undefined) when the example completes.
|
|
48
|
+
#
|
|
49
|
+
# @param constant_name [String] The fully qualified name of the constant. The current
|
|
50
|
+
# constant scoping at the point of call is not considered.
|
|
51
|
+
# @param value [Object] The value to make the constant refer to. When the
|
|
52
|
+
# example completes, the constant will be restored to its prior state.
|
|
53
|
+
# @param options [Hash] Stubbing options.
|
|
54
|
+
# @option options :transfer_nested_constants [Boolean, Array<Symbol>] Determines
|
|
55
|
+
# what nested constants, if any, will be transferred from the original value
|
|
56
|
+
# of the constant to the new value of the constant. This only works if both
|
|
57
|
+
# the original and new values are modules (or classes).
|
|
58
|
+
# @return [Object] the stubbed value of the constant
|
|
59
|
+
#
|
|
60
|
+
# @example
|
|
61
|
+
#
|
|
62
|
+
# stub_const("MyClass", Class.new) # => Replaces (or defines) MyClass with a new class object.
|
|
63
|
+
# stub_const("SomeModel::PER_PAGE", 5) # => Sets SomeModel::PER_PAGE to 5.
|
|
64
|
+
#
|
|
65
|
+
# class CardDeck
|
|
66
|
+
# SUITS = [:Spades, :Diamonds, :Clubs, :Hearts]
|
|
67
|
+
# NUM_CARDS = 52
|
|
68
|
+
# end
|
|
69
|
+
#
|
|
70
|
+
# stub_const("CardDeck", Class.new)
|
|
71
|
+
# CardDeck::SUITS # => uninitialized constant error
|
|
72
|
+
# CardDeck::NUM_CARDS # => uninitialized constant error
|
|
73
|
+
#
|
|
74
|
+
# stub_const("CardDeck", Class.new, :transfer_nested_constants => true)
|
|
75
|
+
# CardDeck::SUITS # => our suits array
|
|
76
|
+
# CardDeck::NUM_CARDS # => 52
|
|
77
|
+
#
|
|
78
|
+
# stub_const("CardDeck", Class.new, :transfer_nested_constants => [:SUITS])
|
|
79
|
+
# CardDeck::SUITS # => our suits array
|
|
80
|
+
# CardDeck::NUM_CARDS # => uninitialized constant error
|
|
81
|
+
def stub_const(constant_name, value, options = {})
|
|
82
|
+
ConstantStubber.stub(constant_name, value, options)
|
|
83
|
+
end
|
|
84
|
+
|
|
44
85
|
private
|
|
45
86
|
|
|
46
87
|
def declare_double(declared_as, *args)
|
|
@@ -9,7 +9,7 @@ require 'rspec/mocks/argument_matchers'
|
|
|
9
9
|
require 'rspec/mocks/proxy'
|
|
10
10
|
require 'rspec/mocks/test_double'
|
|
11
11
|
require 'rspec/mocks/mock'
|
|
12
|
-
require 'rspec/mocks/
|
|
12
|
+
require 'rspec/mocks/argument_list_matcher'
|
|
13
13
|
require 'rspec/mocks/message_expectation'
|
|
14
14
|
require 'rspec/mocks/order_group'
|
|
15
15
|
require 'rspec/mocks/errors'
|
|
@@ -17,3 +17,4 @@ require 'rspec/mocks/error_generator'
|
|
|
17
17
|
require 'rspec/mocks/space'
|
|
18
18
|
require 'rspec/mocks/serialization'
|
|
19
19
|
require 'rspec/mocks/any_instance'
|
|
20
|
+
require 'rspec/mocks/stub_const'
|
|
@@ -3,11 +3,10 @@ module RSpec
|
|
|
3
3
|
|
|
4
4
|
class MessageExpectation
|
|
5
5
|
# @private
|
|
6
|
-
attr_reader :message
|
|
7
|
-
attr_writer :expected_received_count, :expected_from, :argument_expectation, :implementation
|
|
8
|
-
protected :expected_received_count=, :expected_from=, :implementation=
|
|
9
6
|
attr_accessor :error_generator
|
|
10
|
-
|
|
7
|
+
attr_reader :message
|
|
8
|
+
attr_writer :expected_received_count, :expected_from, :argument_list_matcher
|
|
9
|
+
protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=
|
|
11
10
|
|
|
12
11
|
# @private
|
|
13
12
|
def initialize(error_generator, expectation_ordering, expected_from, message, expected_received_count=1, opts={}, &implementation)
|
|
@@ -17,7 +16,7 @@ module RSpec
|
|
|
17
16
|
@message = message
|
|
18
17
|
@actual_received_count = 0
|
|
19
18
|
@expected_received_count = expected_received_count
|
|
20
|
-
@
|
|
19
|
+
@argument_list_matcher = ArgumentListMatcher.new(ArgumentMatchers::AnyArgsMatcher.new)
|
|
21
20
|
@consecutive = false
|
|
22
21
|
@exception_to_raise = nil
|
|
23
22
|
@args_to_throw = []
|
|
@@ -30,6 +29,12 @@ module RSpec
|
|
|
30
29
|
@implementation = implementation
|
|
31
30
|
end
|
|
32
31
|
|
|
32
|
+
def implementation=(implementation)
|
|
33
|
+
@consecutive = false
|
|
34
|
+
@implementation = implementation
|
|
35
|
+
end
|
|
36
|
+
protected :implementation=
|
|
37
|
+
|
|
33
38
|
# @private
|
|
34
39
|
def build_child(expected_from, expected_received_count, opts={}, &implementation)
|
|
35
40
|
child = clone
|
|
@@ -41,13 +46,13 @@ module RSpec
|
|
|
41
46
|
new_gen.opts = opts
|
|
42
47
|
child.error_generator = new_gen
|
|
43
48
|
child.clone_args_to_yield(*@args_to_yield)
|
|
44
|
-
child.
|
|
49
|
+
child.argument_list_matcher = ArgumentListMatcher.new(ArgumentMatchers::AnyArgsMatcher.new)
|
|
45
50
|
child
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
# @private
|
|
49
54
|
def expected_args
|
|
50
|
-
@
|
|
55
|
+
@argument_list_matcher.expected_args
|
|
51
56
|
end
|
|
52
57
|
|
|
53
58
|
# @overload and_return(value)
|
|
@@ -87,7 +92,7 @@ module RSpec
|
|
|
87
92
|
# counter.stub(:count) { 1 }
|
|
88
93
|
# counter.count # => 1
|
|
89
94
|
def and_return(*values, &implementation)
|
|
90
|
-
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args?
|
|
95
|
+
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least)
|
|
91
96
|
@consecutive = true if values.size > 1
|
|
92
97
|
@implementation = implementation || build_implementation(values)
|
|
93
98
|
end
|
|
@@ -110,7 +115,7 @@ module RSpec
|
|
|
110
115
|
# car.stub(:go).and_raise
|
|
111
116
|
# car.stub(:go).and_raise(OutOfGas)
|
|
112
117
|
# car.stub(:go).and_raise(OutOfGas.new(2, :oz))
|
|
113
|
-
def and_raise(exception=
|
|
118
|
+
def and_raise(exception=RuntimeError)
|
|
114
119
|
@exception_to_raise = exception
|
|
115
120
|
end
|
|
116
121
|
|
|
@@ -148,12 +153,12 @@ module RSpec
|
|
|
148
153
|
|
|
149
154
|
# @private
|
|
150
155
|
def matches?(message, *args)
|
|
151
|
-
@message == message && @
|
|
156
|
+
@message == message && @argument_list_matcher.args_match?(*args)
|
|
152
157
|
end
|
|
153
158
|
|
|
154
159
|
# @private
|
|
155
160
|
def invoke(*args, &block)
|
|
156
|
-
if @expected_received_count == 0 || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
|
|
161
|
+
if (@expected_received_count == 0 && !@at_least) || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
|
|
157
162
|
@actual_received_count += 1
|
|
158
163
|
@failed_fast = true
|
|
159
164
|
@error_generator.raise_expectation_error(@message, @expected_received_count, @actual_received_count, *args)
|
|
@@ -194,12 +199,15 @@ MESSAGE
|
|
|
194
199
|
|
|
195
200
|
# @private
|
|
196
201
|
def called_max_times?
|
|
197
|
-
@expected_received_count != :any &&
|
|
202
|
+
@expected_received_count != :any &&
|
|
203
|
+
!@at_least &&
|
|
204
|
+
@expected_received_count > 0 &&
|
|
205
|
+
@actual_received_count >= @expected_received_count
|
|
198
206
|
end
|
|
199
207
|
|
|
200
208
|
# @private
|
|
201
209
|
def matches_name_but_not_args(message, *args)
|
|
202
|
-
@message == message and not @
|
|
210
|
+
@message == message and not @argument_list_matcher.args_match?(*args)
|
|
203
211
|
end
|
|
204
212
|
|
|
205
213
|
# @private
|
|
@@ -248,7 +256,7 @@ MESSAGE
|
|
|
248
256
|
# @private
|
|
249
257
|
def generate_error
|
|
250
258
|
if similar_messages.empty?
|
|
251
|
-
@error_generator.raise_expectation_error(@message, @expected_received_count, @actual_received_count,
|
|
259
|
+
@error_generator.raise_expectation_error(@message, @expected_received_count, @actual_received_count, *expected_args)
|
|
252
260
|
else
|
|
253
261
|
@error_generator.raise_similar_message_args_error(self, *@similar_messages)
|
|
254
262
|
end
|
|
@@ -284,7 +292,7 @@ MESSAGE
|
|
|
284
292
|
# # => passes
|
|
285
293
|
def with(*args, &block)
|
|
286
294
|
@implementation = block if block_given? unless args.empty?
|
|
287
|
-
@
|
|
295
|
+
@argument_list_matcher = ArgumentListMatcher.new(*args, &block)
|
|
288
296
|
self
|
|
289
297
|
end
|
|
290
298
|
|
|
@@ -467,6 +475,10 @@ MESSAGE
|
|
|
467
475
|
super(error_generator, expectation_ordering, expected_from, message, 0, {}, &implementation)
|
|
468
476
|
end
|
|
469
477
|
|
|
478
|
+
def and_return(*)
|
|
479
|
+
# no-op
|
|
480
|
+
end
|
|
481
|
+
|
|
470
482
|
# @private
|
|
471
483
|
def negative_expectation_for?(message)
|
|
472
484
|
return @message == message
|
data/lib/rspec/mocks/methods.rb
CHANGED
data/lib/rspec/mocks/proxy.rb
CHANGED
|
@@ -65,6 +65,7 @@ module RSpec
|
|
|
65
65
|
|
|
66
66
|
# @private
|
|
67
67
|
def add_message_expectation(location, method_name, opts={}, &block)
|
|
68
|
+
block ||= Proc.new { @object } if null_object?
|
|
68
69
|
method_double[method_name].add_expectation @error_generator, @expectation_ordering, location, opts, &block
|
|
69
70
|
end
|
|
70
71
|
|
|
@@ -128,7 +129,7 @@ module RSpec
|
|
|
128
129
|
raise_unexpected_message_args_error(expectation, *args) unless (has_negative_expectation?(message) or null_object?)
|
|
129
130
|
elsif stub = find_almost_matching_stub(message, *args)
|
|
130
131
|
stub.advise(*args)
|
|
131
|
-
|
|
132
|
+
raise_missing_default_stub_error(stub, *args)
|
|
132
133
|
elsif @object.is_a?(Class)
|
|
133
134
|
@object.superclass.__send__(message, *args, &block)
|
|
134
135
|
else
|
|
@@ -136,14 +137,19 @@ module RSpec
|
|
|
136
137
|
end
|
|
137
138
|
end
|
|
138
139
|
|
|
140
|
+
# @private
|
|
141
|
+
def raise_unexpected_message_error(method_name, *args)
|
|
142
|
+
@error_generator.raise_unexpected_message_error method_name, *args
|
|
143
|
+
end
|
|
144
|
+
|
|
139
145
|
# @private
|
|
140
146
|
def raise_unexpected_message_args_error(expectation, *args)
|
|
141
147
|
@error_generator.raise_unexpected_message_args_error(expectation, *args)
|
|
142
148
|
end
|
|
143
149
|
|
|
144
150
|
# @private
|
|
145
|
-
def
|
|
146
|
-
@error_generator.
|
|
151
|
+
def raise_missing_default_stub_error(expectation, *args)
|
|
152
|
+
@error_generator.raise_missing_default_stub_error(expectation, *args)
|
|
147
153
|
end
|
|
148
154
|
|
|
149
155
|
private
|
|
@@ -157,7 +163,7 @@ module RSpec
|
|
|
157
163
|
end
|
|
158
164
|
|
|
159
165
|
def find_matching_expectation(method_name, *args)
|
|
160
|
-
method_double[method_name].expectations.find {|expectation| expectation.matches?(method_name, *args) && !expectation.called_max_times?} ||
|
|
166
|
+
(method_double[method_name].expectations.find {|expectation| expectation.matches?(method_name, *args) && !expectation.called_max_times?}) ||
|
|
161
167
|
method_double[method_name].expectations.find {|expectation| expectation.matches?(method_name, *args)}
|
|
162
168
|
end
|
|
163
169
|
|