rspec-mocks 3.0.0 → 3.1.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.
- checksums.yaml +6 -14
- checksums.yaml.gz.sig +0 -0
- data/Changelog.md +89 -1
- data/README.md +32 -18
- data/lib/rspec/mocks/any_instance/chain.rb +2 -2
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +2 -3
- data/lib/rspec/mocks/any_instance/message_chains.rb +8 -7
- data/lib/rspec/mocks/any_instance/proxy.rb +7 -3
- data/lib/rspec/mocks/any_instance/recorder.rb +27 -25
- data/lib/rspec/mocks/any_instance/stub_chain.rb +3 -5
- data/lib/rspec/mocks/argument_list_matcher.rb +4 -4
- data/lib/rspec/mocks/argument_matchers.rb +39 -6
- data/lib/rspec/mocks/configuration.rb +4 -11
- data/lib/rspec/mocks/error_generator.rb +33 -27
- data/lib/rspec/mocks/example_methods.rb +75 -7
- data/lib/rspec/mocks/instance_method_stasher.rb +5 -5
- data/lib/rspec/mocks/matchers/have_received.rb +10 -8
- data/lib/rspec/mocks/matchers/receive.rb +8 -8
- data/lib/rspec/mocks/matchers/receive_message_chain.rb +5 -6
- data/lib/rspec/mocks/matchers/receive_messages.rb +6 -7
- data/lib/rspec/mocks/message_chain.rb +5 -5
- data/lib/rspec/mocks/message_expectation.rb +66 -27
- data/lib/rspec/mocks/method_double.rb +15 -12
- data/lib/rspec/mocks/method_reference.rb +8 -7
- data/lib/rspec/mocks/mutate_const.rb +26 -100
- data/lib/rspec/mocks/object_reference.rb +12 -13
- data/lib/rspec/mocks/order_group.rb +4 -5
- data/lib/rspec/mocks/proxy.rb +35 -24
- data/lib/rspec/mocks/space.rb +25 -25
- data/lib/rspec/mocks/syntax.rb +101 -99
- data/lib/rspec/mocks/targets.rb +6 -6
- data/lib/rspec/mocks/test_double.rb +13 -4
- data/lib/rspec/mocks/verifying_double.rb +16 -16
- data/lib/rspec/mocks/verifying_message_expecation.rb +15 -13
- data/lib/rspec/mocks/verifying_proxy.rb +12 -16
- data/lib/rspec/mocks/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +61 -54
- metadata.gz.sig +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
module RSpec
|
|
2
2
|
module Mocks
|
|
3
|
-
|
|
4
3
|
# A message expectation that only allows concrete return values to be set
|
|
5
4
|
# for a message. While this same effect can be achieved using a standard
|
|
6
5
|
# MessageExpecation, this version is much faster and so can be used as an
|
|
@@ -8,8 +7,7 @@ module RSpec
|
|
|
8
7
|
#
|
|
9
8
|
# @private
|
|
10
9
|
class SimpleMessageExpectation
|
|
11
|
-
|
|
12
|
-
def initialize(message, response, error_generator, backtrace_line = nil)
|
|
10
|
+
def initialize(message, response, error_generator, backtrace_line=nil)
|
|
13
11
|
@message, @response, @error_generator, @backtrace_line = message.to_sym, response, error_generator, backtrace_line
|
|
14
12
|
@received = false
|
|
15
13
|
end
|
|
@@ -45,6 +43,7 @@ module RSpec
|
|
|
45
43
|
attr_writer :expected_received_count, :expected_from, :argument_list_matcher
|
|
46
44
|
protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
|
|
47
45
|
|
|
46
|
+
# rubocop:disable Style/ParameterLists
|
|
48
47
|
# @private
|
|
49
48
|
def initialize(error_generator, expectation_ordering, expected_from, method_double,
|
|
50
49
|
type=:expectation, opts={}, &implementation_block)
|
|
@@ -59,6 +58,7 @@ module RSpec
|
|
|
59
58
|
@argument_list_matcher = ArgumentListMatcher::MATCH_ALL
|
|
60
59
|
@order_group = expectation_ordering
|
|
61
60
|
@order_group.register(self) unless type == :stub
|
|
61
|
+
@expectation_type = type
|
|
62
62
|
@ordered = false
|
|
63
63
|
@at_least = @at_most = @exactly = nil
|
|
64
64
|
@args_to_yield = []
|
|
@@ -69,6 +69,7 @@ module RSpec
|
|
|
69
69
|
@implementation = Implementation.new
|
|
70
70
|
self.inner_implementation_action = implementation_block
|
|
71
71
|
end
|
|
72
|
+
# rubocop:enable Style/ParameterLists
|
|
72
73
|
|
|
73
74
|
# @private
|
|
74
75
|
def expected_args
|
|
@@ -109,7 +110,7 @@ module RSpec
|
|
|
109
110
|
end
|
|
110
111
|
|
|
111
112
|
values.unshift(first_value)
|
|
112
|
-
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0
|
|
113
|
+
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
|
|
113
114
|
self.terminal_implementation_action = AndReturnImplementation.new(values)
|
|
114
115
|
|
|
115
116
|
nil
|
|
@@ -136,11 +137,30 @@ module RSpec
|
|
|
136
137
|
# counter.increment
|
|
137
138
|
# expect(counter.count).to eq(original_count + 1)
|
|
138
139
|
def and_call_original
|
|
140
|
+
and_wrap_original do |original, *args, &block|
|
|
141
|
+
original.call(*args, &block)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Decorates the stubbed method with the supplied block. The original
|
|
146
|
+
# unmodified method is passed to the block along with any method call
|
|
147
|
+
# arguments so you can delegate to it, whilst still being able to
|
|
148
|
+
# change what args are passed to it and/or change the return value.
|
|
149
|
+
#
|
|
150
|
+
# @note This is only available on partial doubles.
|
|
151
|
+
#
|
|
152
|
+
# @example
|
|
153
|
+
#
|
|
154
|
+
# expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block|
|
|
155
|
+
# original_method.call(*args, &block).first(10)
|
|
156
|
+
# end
|
|
157
|
+
#
|
|
158
|
+
def and_wrap_original(&block)
|
|
139
159
|
if RSpec::Mocks::TestDouble === @method_double.object
|
|
140
160
|
@error_generator.raise_only_valid_on_a_partial_double(:and_call_original)
|
|
141
161
|
else
|
|
142
162
|
warn_about_stub_override if implementation.inner_action
|
|
143
|
-
@implementation =
|
|
163
|
+
@implementation = AndWrapOriginalImplementation.new(@method_double.original_method, block)
|
|
144
164
|
@yield_receiver_to_implementation_block = false
|
|
145
165
|
end
|
|
146
166
|
end
|
|
@@ -166,7 +186,7 @@ module RSpec
|
|
|
166
186
|
# allow(car).to receive(:go).and_raise(OutOfGas)
|
|
167
187
|
# allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive")
|
|
168
188
|
# allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz))
|
|
169
|
-
def and_raise(exception
|
|
189
|
+
def and_raise(exception=RuntimeError, message=nil)
|
|
170
190
|
if exception.respond_to?(:exception)
|
|
171
191
|
exception = message ? exception.exception(message) : exception.exception
|
|
172
192
|
end
|
|
@@ -233,7 +253,7 @@ module RSpec
|
|
|
233
253
|
|
|
234
254
|
# @private
|
|
235
255
|
def matches_name_but_not_args(message, *args)
|
|
236
|
-
@message == message
|
|
256
|
+
@message == message && !@argument_list_matcher.args_match?(*args)
|
|
237
257
|
end
|
|
238
258
|
|
|
239
259
|
# @private
|
|
@@ -295,7 +315,7 @@ module RSpec
|
|
|
295
315
|
def expectation_count_type
|
|
296
316
|
return :at_least if @at_least
|
|
297
317
|
return :at_most if @at_most
|
|
298
|
-
|
|
318
|
+
nil
|
|
299
319
|
end
|
|
300
320
|
|
|
301
321
|
# @private
|
|
@@ -334,7 +354,7 @@ module RSpec
|
|
|
334
354
|
def with(*args, &block)
|
|
335
355
|
if args.empty?
|
|
336
356
|
raise ArgumentError,
|
|
337
|
-
|
|
357
|
+
"`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
|
|
338
358
|
end
|
|
339
359
|
|
|
340
360
|
self.inner_implementation_action = block
|
|
@@ -429,6 +449,17 @@ module RSpec
|
|
|
429
449
|
self
|
|
430
450
|
end
|
|
431
451
|
|
|
452
|
+
# Expect a message to be received exactly three times.
|
|
453
|
+
#
|
|
454
|
+
# @example
|
|
455
|
+
#
|
|
456
|
+
# expect(car).to receive(:go).thrice
|
|
457
|
+
def thrice(&block)
|
|
458
|
+
self.inner_implementation_action = block
|
|
459
|
+
set_expected_received_count :exactly, 3
|
|
460
|
+
self
|
|
461
|
+
end
|
|
462
|
+
|
|
432
463
|
# Expect messages to be received in a specific order.
|
|
433
464
|
#
|
|
434
465
|
# @example
|
|
@@ -438,10 +469,19 @@ module RSpec
|
|
|
438
469
|
# expect(api).to receive(:finish).ordered
|
|
439
470
|
def ordered(&block)
|
|
440
471
|
self.inner_implementation_action = block
|
|
472
|
+
additional_expected_calls.times do
|
|
473
|
+
@order_group.register(self)
|
|
474
|
+
end
|
|
441
475
|
@ordered = true
|
|
442
476
|
self
|
|
443
477
|
end
|
|
444
478
|
|
|
479
|
+
# @private
|
|
480
|
+
def additional_expected_calls
|
|
481
|
+
return 0 if @expectation_type == :stub || !@exactly
|
|
482
|
+
@expected_received_count - 1
|
|
483
|
+
end
|
|
484
|
+
|
|
445
485
|
# @private
|
|
446
486
|
def ordered?
|
|
447
487
|
@ordered
|
|
@@ -465,15 +505,13 @@ module RSpec
|
|
|
465
505
|
private
|
|
466
506
|
|
|
467
507
|
def invoke_incrementing_actual_calls_by(increment, parent_stub, *args, &block)
|
|
468
|
-
if yield_receiver_to_implementation_block?
|
|
469
|
-
args.unshift(orig_object)
|
|
470
|
-
end
|
|
508
|
+
args.unshift(orig_object) if yield_receiver_to_implementation_block?
|
|
471
509
|
|
|
472
510
|
if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
|
|
473
511
|
@actual_received_count += increment
|
|
474
512
|
@failed_fast = true
|
|
475
|
-
#args are the args we actually received, @argument_list_matcher is the
|
|
476
|
-
#list of args we were expecting
|
|
513
|
+
# args are the args we actually received, @argument_list_matcher is the
|
|
514
|
+
# list of args we were expecting
|
|
477
515
|
@error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args)
|
|
478
516
|
end
|
|
479
517
|
|
|
@@ -502,6 +540,7 @@ module RSpec
|
|
|
502
540
|
when Numeric then n
|
|
503
541
|
when :once then 1
|
|
504
542
|
when :twice then 2
|
|
543
|
+
when :thrice then 3
|
|
505
544
|
end
|
|
506
545
|
end
|
|
507
546
|
|
|
@@ -521,7 +560,7 @@ module RSpec
|
|
|
521
560
|
|
|
522
561
|
def warn_about_stub_override
|
|
523
562
|
RSpec.warning(
|
|
524
|
-
"You're overriding a previous stub implementation of `#{@message}`. "
|
|
563
|
+
"You're overriding a previous stub implementation of `#{@message}`. " \
|
|
525
564
|
"Called from #{CallerFilter.first_non_rspec_line}."
|
|
526
565
|
)
|
|
527
566
|
end
|
|
@@ -536,7 +575,7 @@ module RSpec
|
|
|
536
575
|
@error_generator = error_generator
|
|
537
576
|
end
|
|
538
577
|
|
|
539
|
-
def call(*
|
|
578
|
+
def call(*_args_to_ignore, &block)
|
|
540
579
|
return if @args_to_yield.empty? && @eval_context.nil?
|
|
541
580
|
|
|
542
581
|
@error_generator.raise_missing_block_error @args_to_yield unless block
|
|
@@ -544,7 +583,7 @@ module RSpec
|
|
|
544
583
|
block_signature = Support::BlockSignature.new(block)
|
|
545
584
|
|
|
546
585
|
@args_to_yield.each do |args|
|
|
547
|
-
unless Support::
|
|
586
|
+
unless Support::StrictSignatureVerifier.new(block_signature, args).valid?
|
|
548
587
|
@error_generator.raise_wrong_arity_error(args, block_signature)
|
|
549
588
|
end
|
|
550
589
|
|
|
@@ -561,7 +600,7 @@ module RSpec
|
|
|
561
600
|
@values_to_return = values_to_return
|
|
562
601
|
end
|
|
563
602
|
|
|
564
|
-
def call(*
|
|
603
|
+
def call(*_args_to_ignore, &_block)
|
|
565
604
|
if @values_to_return.size > 1
|
|
566
605
|
@values_to_return.shift
|
|
567
606
|
else
|
|
@@ -595,22 +634,23 @@ module RSpec
|
|
|
595
634
|
|
|
596
635
|
# Represents an `and_call_original` implementation.
|
|
597
636
|
# @private
|
|
598
|
-
class
|
|
599
|
-
def initialize(method)
|
|
637
|
+
class AndWrapOriginalImplementation
|
|
638
|
+
def initialize(method, block)
|
|
600
639
|
@method = method
|
|
640
|
+
@block = block
|
|
601
641
|
end
|
|
602
642
|
|
|
603
643
|
CannotModifyFurtherError = Class.new(StandardError)
|
|
604
644
|
|
|
605
|
-
def initial_action=(
|
|
645
|
+
def initial_action=(_value)
|
|
606
646
|
raise cannot_modify_further_error
|
|
607
647
|
end
|
|
608
648
|
|
|
609
|
-
def inner_action=(
|
|
649
|
+
def inner_action=(_value)
|
|
610
650
|
raise cannot_modify_further_error
|
|
611
651
|
end
|
|
612
652
|
|
|
613
|
-
def terminal_action=(
|
|
653
|
+
def terminal_action=(_value)
|
|
614
654
|
raise cannot_modify_further_error
|
|
615
655
|
end
|
|
616
656
|
|
|
@@ -623,13 +663,13 @@ module RSpec
|
|
|
623
663
|
end
|
|
624
664
|
|
|
625
665
|
def call(*args, &block)
|
|
626
|
-
@
|
|
666
|
+
@block.call(@method, *args, &block)
|
|
627
667
|
end
|
|
628
668
|
|
|
629
669
|
private
|
|
630
670
|
|
|
631
671
|
def cannot_modify_further_error
|
|
632
|
-
CannotModifyFurtherError.new "This method has already been configured "
|
|
672
|
+
CannotModifyFurtherError.new "This method has already been configured " \
|
|
633
673
|
"to call the original implementation, and cannot be modified further."
|
|
634
674
|
end
|
|
635
675
|
end
|
|
@@ -642,9 +682,8 @@ module RSpec
|
|
|
642
682
|
yield
|
|
643
683
|
rescue RSpec::Mocks::MockExpectationError => error
|
|
644
684
|
error.backtrace.insert(0, location)
|
|
645
|
-
Kernel
|
|
685
|
+
Kernel.raise error
|
|
646
686
|
end
|
|
647
687
|
end
|
|
648
|
-
|
|
649
688
|
end
|
|
650
689
|
end
|
|
@@ -59,7 +59,7 @@ module RSpec
|
|
|
59
59
|
define_method(method_name) do |*args, &block|
|
|
60
60
|
method_double.proxy_method_invoked(self, *args, &block)
|
|
61
61
|
end
|
|
62
|
-
|
|
62
|
+
__send__(visibility, method_name)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
@method_is_proxied = true
|
|
@@ -69,7 +69,7 @@ module RSpec
|
|
|
69
69
|
# method to perform additional operations.
|
|
70
70
|
#
|
|
71
71
|
# @private
|
|
72
|
-
def proxy_method_invoked(
|
|
72
|
+
def proxy_method_invoked(_obj, *args, &block)
|
|
73
73
|
@proxy.message_received method_name, *args, &block
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -80,9 +80,7 @@ module RSpec
|
|
|
80
80
|
|
|
81
81
|
definition_target.__send__(:remove_method, @method_name)
|
|
82
82
|
|
|
83
|
-
if @method_stasher.method_is_stashed?
|
|
84
|
-
@method_stasher.restore
|
|
85
|
-
end
|
|
83
|
+
@method_stasher.restore if @method_stasher.method_is_stashed?
|
|
86
84
|
restore_original_visibility
|
|
87
85
|
|
|
88
86
|
@method_is_proxied = false
|
|
@@ -91,7 +89,9 @@ module RSpec
|
|
|
91
89
|
# @private
|
|
92
90
|
def show_frozen_warning
|
|
93
91
|
RSpec.warn_with(
|
|
94
|
-
"WARNING: rspec-mocks was unable to restore the original `#{@method_name}`
|
|
92
|
+
"WARNING: rspec-mocks was unable to restore the original `#{@method_name}` " \
|
|
93
|
+
"method on #{@object.inspect} because it has been frozen. If you reuse this " \
|
|
94
|
+
"object, `#{@method_name}` will continue to respond with its stub implementation.",
|
|
95
95
|
:call_site => nil,
|
|
96
96
|
:use_spec_location_as_call_site => true
|
|
97
97
|
)
|
|
@@ -107,7 +107,7 @@ module RSpec
|
|
|
107
107
|
|
|
108
108
|
# @private
|
|
109
109
|
def verify
|
|
110
|
-
expectations.each {|e| e.verify_messages_received}
|
|
110
|
+
expectations.each { |e| e.verify_messages_received }
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
# @private
|
|
@@ -134,7 +134,7 @@ module RSpec
|
|
|
134
134
|
def add_expectation(error_generator, expectation_ordering, expected_from, opts, &implementation)
|
|
135
135
|
configure_method
|
|
136
136
|
expectation = message_expectation_class.new(error_generator, expectation_ordering,
|
|
137
|
-
|
|
137
|
+
expected_from, self, :expectation, opts, &implementation)
|
|
138
138
|
expectations << expectation
|
|
139
139
|
expectation
|
|
140
140
|
end
|
|
@@ -149,7 +149,7 @@ module RSpec
|
|
|
149
149
|
def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation)
|
|
150
150
|
configure_method
|
|
151
151
|
stub = message_expectation_class.new(error_generator, expectation_ordering, expected_from,
|
|
152
|
-
|
|
152
|
+
self, :stub, opts, &implementation)
|
|
153
153
|
stubs.unshift stub
|
|
154
154
|
stub
|
|
155
155
|
end
|
|
@@ -172,7 +172,7 @@ module RSpec
|
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
# @private
|
|
175
|
-
def setup_simple_method_double(method_name, response, collection, error_generator
|
|
175
|
+
def setup_simple_method_double(method_name, response, collection, error_generator=nil, backtrace_line=nil)
|
|
176
176
|
define_proxy_method
|
|
177
177
|
|
|
178
178
|
me = SimpleMessageExpectation.new(method_name, response, error_generator, backtrace_line)
|
|
@@ -202,8 +202,6 @@ module RSpec
|
|
|
202
202
|
raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
|
|
203
203
|
end
|
|
204
204
|
|
|
205
|
-
private
|
|
206
|
-
|
|
207
205
|
# In Ruby 2.0.0 and above prepend will alter the method lookup chain.
|
|
208
206
|
# We use an object's singleton class to define method doubles upon,
|
|
209
207
|
# however if the object has had it's singleton class (as opposed to
|
|
@@ -216,6 +214,9 @@ module RSpec
|
|
|
216
214
|
# of our own.
|
|
217
215
|
#
|
|
218
216
|
if Support::RubyFeatures.module_prepends_supported?
|
|
217
|
+
|
|
218
|
+
private
|
|
219
|
+
|
|
219
220
|
# We subclass `Module` in order to be able to easily detect our prepended module.
|
|
220
221
|
RSpecPrependedModule = Class.new(Module)
|
|
221
222
|
|
|
@@ -247,6 +248,8 @@ module RSpec
|
|
|
247
248
|
|
|
248
249
|
else
|
|
249
250
|
|
|
251
|
+
private
|
|
252
|
+
|
|
250
253
|
def definition_target
|
|
251
254
|
object_singleton_class
|
|
252
255
|
end
|
|
@@ -27,7 +27,7 @@ module RSpec
|
|
|
27
27
|
# cases when we don't know if a method is implemented and
|
|
28
28
|
# both `implemented?` and `unimplemented?` will return false.
|
|
29
29
|
def unimplemented?
|
|
30
|
-
@object_reference.when_loaded do |
|
|
30
|
+
@object_reference.when_loaded do |_m|
|
|
31
31
|
return !implemented?
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -44,9 +44,8 @@ module RSpec
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def with_signature
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
end
|
|
47
|
+
return unless (original = original_method)
|
|
48
|
+
yield Support::MethodSignature.new(original)
|
|
50
49
|
end
|
|
51
50
|
|
|
52
51
|
def visibility
|
|
@@ -59,7 +58,7 @@ module RSpec
|
|
|
59
58
|
:public
|
|
60
59
|
end
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
private
|
|
63
62
|
|
|
64
63
|
def original_method
|
|
65
64
|
@object_reference.when_loaded do |m|
|
|
@@ -98,7 +97,8 @@ module RSpec
|
|
|
98
97
|
|
|
99
98
|
# @private
|
|
100
99
|
class InstanceMethodReference < MethodReference
|
|
101
|
-
|
|
100
|
+
private
|
|
101
|
+
|
|
102
102
|
def method_implemented?(mod)
|
|
103
103
|
MethodReference.method_defined_at_any_visibility?(mod, @method_name)
|
|
104
104
|
end
|
|
@@ -133,7 +133,8 @@ module RSpec
|
|
|
133
133
|
|
|
134
134
|
# @private
|
|
135
135
|
class ObjectMethodReference < MethodReference
|
|
136
|
-
|
|
136
|
+
private
|
|
137
|
+
|
|
137
138
|
def method_implemented?(object)
|
|
138
139
|
object.respond_to?(@method_name, true)
|
|
139
140
|
end
|
|
@@ -1,85 +1,11 @@
|
|
|
1
|
+
RSpec::Support.require_rspec_support 'recursive_const_methods'
|
|
2
|
+
|
|
1
3
|
module RSpec
|
|
2
4
|
module Mocks
|
|
3
|
-
# Provides recursive constant lookup methods useful for
|
|
4
|
-
# constant stubbing.
|
|
5
|
-
#
|
|
6
|
-
# @private
|
|
7
|
-
module RecursiveConstMethods
|
|
8
|
-
# We only want to consider constants that are defined directly on a
|
|
9
|
-
# particular module, and not include top-level/inherited constants.
|
|
10
|
-
# Unfortunately, the constant API changed between 1.8 and 1.9, so
|
|
11
|
-
# we need to conditionally define methods to ignore the top-level/inherited
|
|
12
|
-
# constants.
|
|
13
|
-
#
|
|
14
|
-
# Given:
|
|
15
|
-
# class A; B = 1; end
|
|
16
|
-
# class C < A; end
|
|
17
|
-
#
|
|
18
|
-
# On 1.8:
|
|
19
|
-
# - C.const_get("Hash") # => ::Hash
|
|
20
|
-
# - C.const_defined?("Hash") # => false
|
|
21
|
-
# - C.constants # => ["B"]
|
|
22
|
-
# - None of these methods accept the extra `inherit` argument
|
|
23
|
-
# On 1.9:
|
|
24
|
-
# - C.const_get("Hash") # => ::Hash
|
|
25
|
-
# - C.const_defined?("Hash") # => true
|
|
26
|
-
# - C.const_get("Hash", false) # => raises NameError
|
|
27
|
-
# - C.const_defined?("Hash", false) # => false
|
|
28
|
-
# - C.constants # => [:B]
|
|
29
|
-
# - C.constants(false) #=> []
|
|
30
|
-
if Module.method(:const_defined?).arity == 1
|
|
31
|
-
def const_defined_on?(mod, const_name)
|
|
32
|
-
mod.const_defined?(const_name)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def get_const_defined_on(mod, const_name)
|
|
36
|
-
if const_defined_on?(mod, const_name)
|
|
37
|
-
return mod.const_get(const_name)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
raise NameError, "uninitialized constant #{mod.name}::#{const_name}"
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def constants_defined_on(mod)
|
|
44
|
-
mod.constants.select { |c| const_defined_on?(mod, c) }
|
|
45
|
-
end
|
|
46
|
-
else
|
|
47
|
-
def const_defined_on?(mod, const_name)
|
|
48
|
-
mod.const_defined?(const_name, false)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def get_const_defined_on(mod, const_name)
|
|
52
|
-
mod.const_get(const_name, false)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def constants_defined_on(mod)
|
|
56
|
-
mod.constants(false)
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def recursive_const_get(const_name)
|
|
61
|
-
normalize_const_name(const_name).split('::').inject(Object) do |mod, name|
|
|
62
|
-
get_const_defined_on(mod, name)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def recursive_const_defined?(const_name)
|
|
67
|
-
normalize_const_name(const_name).split('::').inject([Object, '']) do |(mod, full_name), name|
|
|
68
|
-
yield(full_name, name) if block_given? && !(Module === mod)
|
|
69
|
-
return false unless const_defined_on?(mod, name)
|
|
70
|
-
[get_const_defined_on(mod, name), [mod, name].join('::')]
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def normalize_const_name(const_name)
|
|
75
|
-
const_name.sub(/\A::/, '')
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
5
|
# Provides information about constants that may (or may not)
|
|
80
6
|
# have been mutated by rspec-mocks.
|
|
81
7
|
class Constant
|
|
82
|
-
extend RecursiveConstMethods
|
|
8
|
+
extend Support::RecursiveConstMethods
|
|
83
9
|
|
|
84
10
|
# @api private
|
|
85
11
|
def initialize(name)
|
|
@@ -154,7 +80,7 @@ module RSpec
|
|
|
154
80
|
|
|
155
81
|
# Provides a means to stub constants.
|
|
156
82
|
class ConstantMutator
|
|
157
|
-
extend RecursiveConstMethods
|
|
83
|
+
extend Support::RecursiveConstMethods
|
|
158
84
|
|
|
159
85
|
# Stubs a constant.
|
|
160
86
|
#
|
|
@@ -167,12 +93,12 @@ module RSpec
|
|
|
167
93
|
# examples. This is an alternate public API that is provided
|
|
168
94
|
# so you can stub constants in other contexts (e.g. helper
|
|
169
95
|
# classes).
|
|
170
|
-
def self.stub(constant_name, value, options
|
|
96
|
+
def self.stub(constant_name, value, options={})
|
|
171
97
|
mutator = if recursive_const_defined?(constant_name, &raise_on_invalid_const)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
98
|
+
DefinedConstantReplacer
|
|
99
|
+
else
|
|
100
|
+
UndefinedConstantSetter
|
|
101
|
+
end
|
|
176
102
|
|
|
177
103
|
mutate(mutator.new(constant_name, value, options[:transfer_nested_constants]))
|
|
178
104
|
value
|
|
@@ -188,7 +114,7 @@ module RSpec
|
|
|
188
114
|
# so you can hide constants in other contexts (e.g. helper
|
|
189
115
|
# classes).
|
|
190
116
|
def self.hide(constant_name)
|
|
191
|
-
mutate(ConstantHider.new(constant_name, nil, {
|
|
117
|
+
mutate(ConstantHider.new(constant_name, nil, {}))
|
|
192
118
|
nil
|
|
193
119
|
end
|
|
194
120
|
|
|
@@ -196,7 +122,7 @@ module RSpec
|
|
|
196
122
|
#
|
|
197
123
|
# @private
|
|
198
124
|
class BaseMutator
|
|
199
|
-
include RecursiveConstMethods
|
|
125
|
+
include Support::RecursiveConstMethods
|
|
200
126
|
|
|
201
127
|
attr_reader :original_value, :full_constant_name
|
|
202
128
|
|
|
@@ -227,7 +153,7 @@ module RSpec
|
|
|
227
153
|
# @private
|
|
228
154
|
class ConstantHider < BaseMutator
|
|
229
155
|
def mutate
|
|
230
|
-
return unless @defined = recursive_const_defined?(full_constant_name)
|
|
156
|
+
return unless (@defined = recursive_const_defined?(full_constant_name))
|
|
231
157
|
@context = recursive_const_get(@context_parts.join('::'))
|
|
232
158
|
@original_value = get_const_defined_on(@context, @const_name)
|
|
233
159
|
|
|
@@ -298,12 +224,12 @@ module RSpec
|
|
|
298
224
|
return [] unless should_transfer_nested_constants?
|
|
299
225
|
|
|
300
226
|
{ @original_value => "the original value", @mutated_value => "the stubbed value" }.each do |value, description|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
227
|
+
next if value.respond_to?(:constants)
|
|
228
|
+
|
|
229
|
+
raise ArgumentError,
|
|
230
|
+
"Cannot transfer nested constants for #{@full_constant_name} " \
|
|
231
|
+
"since #{description} is not a class or module and only classes " \
|
|
232
|
+
"and modules support nested constants."
|
|
307
233
|
end
|
|
308
234
|
|
|
309
235
|
if Array === @transfer_nested_constants
|
|
@@ -313,9 +239,9 @@ module RSpec
|
|
|
313
239
|
if undefined_constants.any?
|
|
314
240
|
available_constants = constants_defined_on(@original_value) - @transfer_nested_constants
|
|
315
241
|
raise ArgumentError,
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
242
|
+
"Cannot transfer nested constant(s) #{undefined_constants.join(' and ')} " \
|
|
243
|
+
"for #{@full_constant_name} since they are not defined. Did you mean " \
|
|
244
|
+
"#{available_constants.join(' or ')}?"
|
|
319
245
|
end
|
|
320
246
|
|
|
321
247
|
@transfer_nested_constants
|
|
@@ -363,10 +289,10 @@ module RSpec
|
|
|
363
289
|
|
|
364
290
|
def name_for(parent, name)
|
|
365
291
|
root = if parent == Object
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
292
|
+
''
|
|
293
|
+
else
|
|
294
|
+
parent.name
|
|
295
|
+
end
|
|
370
296
|
root + '::' + name
|
|
371
297
|
end
|
|
372
298
|
end
|
|
@@ -389,7 +315,7 @@ module RSpec
|
|
|
389
315
|
# @api private
|
|
390
316
|
def self.raise_on_invalid_const
|
|
391
317
|
lambda do |const_name, failed_name|
|
|
392
|
-
raise "Cannot stub constant #{failed_name} on #{const_name} "
|
|
318
|
+
raise "Cannot stub constant #{failed_name} on #{const_name} " \
|
|
393
319
|
"since #{const_name} is not a module."
|
|
394
320
|
end
|
|
395
321
|
end
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
module RSpec
|
|
2
2
|
module Mocks
|
|
3
|
-
|
|
4
3
|
# @private
|
|
5
4
|
class ObjectReference
|
|
6
5
|
# Returns an appropriate Object or Module reference based
|
|
7
6
|
# on the given argument.
|
|
8
|
-
def self.for(object_module_or_name, allow_direct_object_refs
|
|
7
|
+
def self.for(object_module_or_name, allow_direct_object_refs=false)
|
|
9
8
|
case object_module_or_name
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
when Module then DirectModuleReference.new(object_module_or_name)
|
|
10
|
+
when String then NamedObjectReference.new(object_module_or_name)
|
|
11
|
+
else
|
|
12
|
+
if allow_direct_object_refs
|
|
13
|
+
DirectObjectReference.new(object_module_or_name)
|
|
12
14
|
else
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
raise ArgumentError,
|
|
17
|
-
"Module or String expected, got #{object_module_or_name.inspect}"
|
|
18
|
-
end
|
|
15
|
+
raise ArgumentError,
|
|
16
|
+
"Module or String expected, got #{object_module_or_name.inspect}"
|
|
17
|
+
end
|
|
19
18
|
end
|
|
20
19
|
end
|
|
21
20
|
end
|
|
@@ -35,7 +34,7 @@ module RSpec
|
|
|
35
34
|
|
|
36
35
|
def const_to_replace
|
|
37
36
|
raise ArgumentError,
|
|
38
|
-
|
|
37
|
+
"Can not perform constant replacement with an object."
|
|
39
38
|
end
|
|
40
39
|
|
|
41
40
|
def defined?
|
|
@@ -78,11 +77,11 @@ module RSpec
|
|
|
78
77
|
end
|
|
79
78
|
alias description const_to_replace
|
|
80
79
|
|
|
81
|
-
def when_loaded(&
|
|
80
|
+
def when_loaded(&_block)
|
|
82
81
|
yield object if object
|
|
83
82
|
end
|
|
84
83
|
|
|
85
|
-
|
|
84
|
+
private
|
|
86
85
|
|
|
87
86
|
def object
|
|
88
87
|
@object ||= Constant.original(@const_name).original_value
|
|
@@ -25,10 +25,10 @@ module RSpec
|
|
|
25
25
|
# @private
|
|
26
26
|
def consume
|
|
27
27
|
remaining_expectations.each_with_index do |expectation, index|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
next unless expectation.ordered?
|
|
29
|
+
|
|
30
|
+
@index += index + 1
|
|
31
|
+
return expectation
|
|
32
32
|
end
|
|
33
33
|
nil
|
|
34
34
|
end
|
|
@@ -76,7 +76,6 @@ module RSpec
|
|
|
76
76
|
def expectation_for(message)
|
|
77
77
|
@expectations.find { |e| message == e }
|
|
78
78
|
end
|
|
79
|
-
|
|
80
79
|
end
|
|
81
80
|
end
|
|
82
81
|
end
|