rspec-mocks 2.10.1 → 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 +26 -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.rb +37 -1
- 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/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 +21 -13
- 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 +14 -0
- data/spec/rspec/mocks/block_return_value_spec.rb +8 -0
- data/spec/rspec/mocks/mock_spec.rb +33 -20
- 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 +45 -45
- data/spec/rspec/mocks/stub_const_spec.rb +309 -0
- data/spec/rspec/mocks/stub_spec.rb +2 -2
- data/spec/spec_helper.rb +0 -40
- metadata +18 -6
- data/lib/rspec/mocks/argument_expectation.rb +0 -52
@@ -2,32 +2,32 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module RSpec
|
4
4
|
module Mocks
|
5
|
-
describe
|
5
|
+
describe ArgumentListMatcher do
|
6
6
|
|
7
7
|
it "considers an object that responds to #matches? and #failure_message_for_should to be a matcher" do
|
8
|
-
|
8
|
+
argument_expectation = RSpec::Mocks::ArgumentListMatcher.new
|
9
9
|
obj = double("matcher")
|
10
10
|
obj.stub(:respond_to?).with(:matches?).and_return(true)
|
11
11
|
obj.stub(:respond_to?).with(:failure_message_for_should).and_return(true)
|
12
|
-
|
12
|
+
argument_expectation.send(:is_matcher?, obj).should be_true
|
13
13
|
end
|
14
14
|
|
15
15
|
it "considers an object that responds to #matches? and #failure_message to be a matcher for backward compatibility" do
|
16
|
-
|
16
|
+
argument_expectation = RSpec::Mocks::ArgumentListMatcher.new
|
17
17
|
obj = double("matcher")
|
18
18
|
obj.stub(:respond_to?).with(:matches?).and_return(true)
|
19
19
|
obj.stub(:respond_to?).with(:failure_message_for_should).and_return(false)
|
20
20
|
obj.stub(:respond_to?).with(:failure_message).and_return(true)
|
21
|
-
|
21
|
+
argument_expectation.send(:is_matcher?, obj).should be_true
|
22
22
|
end
|
23
23
|
|
24
24
|
it "does NOT consider an object that only responds to #matches? to be a matcher" do
|
25
|
-
|
25
|
+
argument_expectation = RSpec::Mocks::ArgumentListMatcher.new
|
26
26
|
obj = double("matcher")
|
27
27
|
obj.stub(:respond_to?).with(:matches?).and_return(true)
|
28
28
|
obj.stub(:respond_to?).with(:failure_message_for_should).and_return(false)
|
29
29
|
obj.stub(:respond_to?).with(:failure_message).and_return(false)
|
30
|
-
|
30
|
+
argument_expectation.send(:is_matcher?, obj).should be_false
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
@@ -123,6 +123,20 @@ module RSpec
|
|
123
123
|
it "passes with at_least(0) with and_return if never called" do
|
124
124
|
@double.should_receive(:do_something).at_least(0).times.and_return true
|
125
125
|
end
|
126
|
+
|
127
|
+
it "uses a stub value if no value set" do
|
128
|
+
@double.stub(:do_something => 'foo')
|
129
|
+
@double.should_receive(:do_something).at_least(:once)
|
130
|
+
@double.do_something.should eq 'foo'
|
131
|
+
@double.do_something.should eq 'foo'
|
132
|
+
end
|
133
|
+
|
134
|
+
it "prefers its own return value over a stub" do
|
135
|
+
@double.stub(:do_something => 'foo')
|
136
|
+
@double.should_receive(:do_something).at_least(:once).and_return('bar')
|
137
|
+
@double.do_something.should eq 'bar'
|
138
|
+
@double.do_something.should eq 'bar'
|
139
|
+
end
|
126
140
|
end
|
127
141
|
end
|
128
142
|
end
|
@@ -7,6 +7,14 @@ describe "a double declaration with a block handed to:" do
|
|
7
7
|
obj.should_receive(:foo) { 'bar' }
|
8
8
|
obj.foo.should eq('bar')
|
9
9
|
end
|
10
|
+
|
11
|
+
it "works when a multi-return stub has already been set" do
|
12
|
+
obj = Object.new
|
13
|
+
return_value = Object.new
|
14
|
+
obj.stub(:foo).and_return(return_value, nil)
|
15
|
+
obj.should_receive(:foo) { return_value }
|
16
|
+
obj.foo.should be(return_value)
|
17
|
+
end
|
10
18
|
end
|
11
19
|
|
12
20
|
describe "stub" do
|
@@ -3,11 +3,17 @@ require 'spec_helper'
|
|
3
3
|
module RSpec
|
4
4
|
module Mocks
|
5
5
|
describe Mock do
|
6
|
-
treats_method_missing_as_private :subject => RSpec::Mocks::Mock.new, :noop => false
|
7
|
-
|
8
6
|
before(:each) { @double = double("test double") }
|
9
7
|
after(:each) { @double.rspec_reset }
|
10
8
|
|
9
|
+
it "has method_missing as private" do
|
10
|
+
RSpec::Mocks::Mock.private_instance_methods.should include_method(:method_missing)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "does not respond_to? method_missing (because it's private)" do
|
14
|
+
RSpec::Mocks::Mock.new.should_not respond_to(:method_missing)
|
15
|
+
end
|
16
|
+
|
11
17
|
it "reports line number of expectation of unreceived message" do
|
12
18
|
expected_error_line = __LINE__; @double.should_receive(:wont_happen).with("x", 3)
|
13
19
|
begin
|
@@ -176,26 +182,28 @@ module RSpec
|
|
176
182
|
}.to raise_error(RSpec::Expectations::ExpectationNotMetError)
|
177
183
|
end
|
178
184
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
+
context "with Ruby > 1.8.6", :unless => RUBY_VERSION.to_s == '1.8.6' do
|
186
|
+
it "passes proc to expectation block without an argument" do
|
187
|
+
# We eval this because Ruby 1.8.6's syntax parser barfs on { |&block| ... }
|
188
|
+
# and prevents the entire spec suite from running.
|
189
|
+
eval("@double.should_receive(:foo) {|&block| block.call.should eq(:bar)}")
|
190
|
+
@double.foo { :bar }
|
191
|
+
end
|
185
192
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
193
|
+
it "passes proc to expectation block with an argument" do
|
194
|
+
eval("@double.should_receive(:foo) {|arg, &block| block.call.should eq(:bar)}")
|
195
|
+
@double.foo(:arg) { :bar }
|
196
|
+
end
|
190
197
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
198
|
+
it "passes proc to stub block without an argurment" do
|
199
|
+
eval("@double.stub(:foo) {|&block| block.call.should eq(:bar)}")
|
200
|
+
@double.foo { :bar }
|
201
|
+
end
|
195
202
|
|
196
|
-
|
197
|
-
|
198
|
-
|
203
|
+
it "passes proc to stub block with an argument" do
|
204
|
+
eval("@double.stub(:foo) {|arg, &block| block.call.should eq(:bar)}")
|
205
|
+
@double.foo(:arg) { :bar }
|
206
|
+
end
|
199
207
|
end
|
200
208
|
|
201
209
|
it "fails right away when method defined as never is received" do
|
@@ -207,7 +215,12 @@ module RSpec
|
|
207
215
|
end
|
208
216
|
|
209
217
|
it "raises when told to" do
|
210
|
-
@double.should_receive(:something).and_raise(
|
218
|
+
@double.should_receive(:something).and_raise(StandardError)
|
219
|
+
expect { @double.something }.to raise_error(StandardError)
|
220
|
+
end
|
221
|
+
|
222
|
+
it "raises RuntimeError by default" do
|
223
|
+
@double.should_receive(:something).and_raise
|
211
224
|
expect { @double.something }.to raise_error(RuntimeError)
|
212
225
|
end
|
213
226
|
|
@@ -75,10 +75,10 @@ module RSpec
|
|
75
75
|
context "when method is stubbed too" do
|
76
76
|
before { @double.stub(:do_something).and_return :stub_result }
|
77
77
|
|
78
|
-
it "uses the
|
78
|
+
it "uses the last value for subsequent calls" do
|
79
79
|
@double.do_something.should equal(11)
|
80
80
|
@double.do_something.should equal(22)
|
81
|
-
@double.do_something.should equal(
|
81
|
+
@double.do_something.should equal(22)
|
82
82
|
@double.rspec_verify
|
83
83
|
end
|
84
84
|
|
@@ -15,6 +15,15 @@ module RSpec
|
|
15
15
|
@double.stub(:foo)
|
16
16
|
@double.should respond_to(:foo)
|
17
17
|
end
|
18
|
+
|
19
|
+
it "raises an error when interpolated in a string as an integer" do
|
20
|
+
# Not sure why, but 1.9.2 raises a different error than 1.8.7 and 1.9.3...
|
21
|
+
expected_error = RUBY_VERSION == '1.9.2' ?
|
22
|
+
RSpec::Mocks::MockExpectationError :
|
23
|
+
TypeError
|
24
|
+
|
25
|
+
expect { "%i" % @double }.to raise_error(expected_error)
|
26
|
+
end
|
18
27
|
end
|
19
28
|
|
20
29
|
describe "a double acting as a null object" do
|
@@ -36,6 +45,11 @@ module RSpec
|
|
36
45
|
@double.something
|
37
46
|
end
|
38
47
|
|
48
|
+
it 'continues to return self from an explicit expectation' do
|
49
|
+
@double.should_receive(:bar)
|
50
|
+
@double.foo.bar.should be(@double)
|
51
|
+
end
|
52
|
+
|
39
53
|
it "fails verification when explicit exception not met" do
|
40
54
|
lambda do
|
41
55
|
@double.should_receive(:something)
|
@@ -59,6 +73,14 @@ module RSpec
|
|
59
73
|
@double.message(:expected_arg)
|
60
74
|
@double.message(:unexpected_arg)
|
61
75
|
end
|
76
|
+
|
77
|
+
it "can be interpolated in a string as an integer" do
|
78
|
+
# This form of string interpolation calls
|
79
|
+
# @double.to_int.to_int.to_int...etc until it gets an integer,
|
80
|
+
# and thus gets stuck in an infinite loop unless our double
|
81
|
+
# returns an int value from #to_int.
|
82
|
+
("%i" % @double).should eq("0")
|
83
|
+
end
|
62
84
|
end
|
63
85
|
|
64
86
|
describe "#as_null_object" do
|
@@ -3,27 +3,27 @@ require 'spec_helper'
|
|
3
3
|
module RSpec
|
4
4
|
module Mocks
|
5
5
|
describe "A chained method stub" do
|
6
|
-
|
6
|
+
let(:object) { Object.new }
|
7
7
|
|
8
8
|
context "with one method in chain" do
|
9
9
|
context "using and_return" do
|
10
10
|
it "returns expected value from chaining only one method call" do
|
11
|
-
|
12
|
-
|
11
|
+
object.stub_chain(:msg1).and_return(:return_value)
|
12
|
+
object.msg1.should equal(:return_value)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
context "using a block" do
|
17
17
|
it "returns the correct value" do
|
18
|
-
|
19
|
-
|
18
|
+
object.stub_chain(:msg1) { :return_value }
|
19
|
+
object.msg1.should equal(:return_value)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
context "using a hash" do
|
24
24
|
it "returns the value of the key/value pair" do
|
25
|
-
|
26
|
-
|
25
|
+
object.stub_chain(:msg1 => :return_value)
|
26
|
+
object.msg1.should equal(:return_value)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -31,22 +31,22 @@ module RSpec
|
|
31
31
|
context "with two methods in chain" do
|
32
32
|
context "using and_return" do
|
33
33
|
it "returns expected value from chaining two method calls" do
|
34
|
-
|
35
|
-
|
34
|
+
object.stub_chain(:msg1, :msg2).and_return(:return_value)
|
35
|
+
object.msg1.msg2.should equal(:return_value)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
39
|
context "using a block" do
|
40
40
|
it "returns the correct value" do
|
41
|
-
|
42
|
-
|
41
|
+
object.stub_chain(:msg1, :msg2) { :return_value }
|
42
|
+
object.msg1.msg2.should equal(:return_value)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
context "using a hash" do
|
47
47
|
it "returns the value of the key/value pair" do
|
48
|
-
|
49
|
-
|
48
|
+
object.stub_chain(:msg1, :msg2 => :return_value)
|
49
|
+
object.msg1.msg2.should equal(:return_value)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
@@ -54,57 +54,57 @@ module RSpec
|
|
54
54
|
context "with four methods in chain" do
|
55
55
|
context "using and_return" do
|
56
56
|
it "returns expected value from chaining two method calls" do
|
57
|
-
|
58
|
-
|
57
|
+
object.stub_chain(:msg1, :msg2, :msg3, :msg4).and_return(:return_value)
|
58
|
+
object.msg1.msg2.msg3.msg4.should equal(:return_value)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
62
|
context "using a block" do
|
63
63
|
it "returns the correct value" do
|
64
|
-
|
65
|
-
|
64
|
+
object.stub_chain(:msg1, :msg2, :msg3, :msg4) { :return_value }
|
65
|
+
object.msg1.msg2.msg3.msg4.should equal(:return_value)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
69
|
context "using a hash" do
|
70
70
|
it "returns the value of the key/value pair" do
|
71
|
-
|
72
|
-
|
71
|
+
object.stub_chain(:msg1, :msg2, :msg3, :msg4 => :return_value)
|
72
|
+
object.msg1.msg2.msg3.msg4.should equal(:return_value)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
76
|
context "using a hash with a string key" do
|
77
77
|
it "returns the value of the key/value pair" do
|
78
|
-
|
79
|
-
|
78
|
+
object.stub_chain("msg1.msg2.msg3.msg4" => :return_value)
|
79
|
+
object.msg1.msg2.msg3.msg4.should equal(:return_value)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
84
|
it "returns expected value from chaining four method calls" do
|
85
|
-
|
86
|
-
|
85
|
+
object.stub_chain(:msg1, :msg2, :msg3, :msg4).and_return(:return_value)
|
86
|
+
object.msg1.msg2.msg3.msg4.should equal(:return_value)
|
87
87
|
end
|
88
88
|
|
89
89
|
context "with messages shared across multiple chains" do
|
90
90
|
context "using and_return" do
|
91
91
|
context "starting with the same message" do
|
92
92
|
it "returns expected value" do
|
93
|
-
|
94
|
-
|
93
|
+
object.stub_chain(:msg1, :msg2, :msg3).and_return(:first)
|
94
|
+
object.stub_chain(:msg1, :msg2, :msg4).and_return(:second)
|
95
95
|
|
96
|
-
|
97
|
-
|
96
|
+
object.msg1.msg2.msg3.should equal(:first)
|
97
|
+
object.msg1.msg2.msg4.should equal(:second)
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
101
|
context "starting with the different messages" do
|
102
102
|
it "returns expected value" do
|
103
|
-
|
104
|
-
|
103
|
+
object.stub_chain(:msg1, :msg2, :msg3).and_return(:first)
|
104
|
+
object.stub_chain(:msg4, :msg2, :msg3).and_return(:second)
|
105
105
|
|
106
|
-
|
107
|
-
|
106
|
+
object.msg1.msg2.msg3.should equal(:first)
|
107
|
+
object.msg4.msg2.msg3.should equal(:second)
|
108
108
|
end
|
109
109
|
end
|
110
110
|
end
|
@@ -112,37 +112,37 @@ module RSpec
|
|
112
112
|
context "using => value" do
|
113
113
|
context "starting with the same message" do
|
114
114
|
it "returns expected value" do
|
115
|
-
|
116
|
-
|
115
|
+
object.stub_chain(:msg1, :msg2, :msg3 => :first)
|
116
|
+
object.stub_chain(:msg1, :msg2, :msg4 => :second)
|
117
117
|
|
118
|
-
|
119
|
-
|
118
|
+
object.msg1.msg2.msg3.should equal(:first)
|
119
|
+
object.msg1.msg2.msg4.should equal(:second)
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
123
123
|
context "starting with different messages" do
|
124
124
|
it "returns expected value" do
|
125
|
-
|
126
|
-
|
125
|
+
object.stub_chain(:msg1, :msg2, :msg3 => :first)
|
126
|
+
object.stub_chain(:msg4, :msg2, :msg3 => :second)
|
127
127
|
|
128
|
-
|
129
|
-
|
128
|
+
object.msg1.msg2.msg3.should equal(:first)
|
129
|
+
object.msg4.msg2.msg3.should equal(:second)
|
130
130
|
end
|
131
131
|
end
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
135
135
|
it "returns expected value when chain is a dot separated string, like stub_chain('msg1.msg2.msg3')" do
|
136
|
-
|
137
|
-
|
136
|
+
object.stub_chain("msg1.msg2.msg3").and_return(:return_value)
|
137
|
+
object.msg1.msg2.msg3.should equal(:return_value)
|
138
138
|
end
|
139
139
|
|
140
140
|
it "returns expected value from two chains with shared messages at the beginning" do
|
141
|
-
|
142
|
-
|
141
|
+
object.stub_chain(:msg1, :msg2, :msg3, :msg4).and_return(:first)
|
142
|
+
object.stub_chain(:msg1, :msg2, :msg3, :msg5).and_return(:second)
|
143
143
|
|
144
|
-
|
145
|
-
|
144
|
+
object.msg1.msg2.msg3.msg4.should equal(:first)
|
145
|
+
object.msg1.msg2.msg3.msg5.should equal(:second)
|
146
146
|
end
|
147
147
|
end
|
148
148
|
end
|
@@ -0,0 +1,309 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
TOP_LEVEL_VALUE_CONST = 7
|
4
|
+
|
5
|
+
class TestClass
|
6
|
+
M = :m
|
7
|
+
N = :n
|
8
|
+
|
9
|
+
class Nested
|
10
|
+
class NestedEvenMore
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module RSpec
|
16
|
+
module Mocks
|
17
|
+
describe "Constant Stubbing" do
|
18
|
+
include RSpec::Mocks::RecursiveConstMethods
|
19
|
+
|
20
|
+
def reset_rspec_mocks
|
21
|
+
::RSpec::Mocks.space.reset_all
|
22
|
+
end
|
23
|
+
|
24
|
+
shared_examples_for "loaded constant stubbing" do |const_name|
|
25
|
+
let!(:original_const_value) { const }
|
26
|
+
after { change_const_value_to(original_const_value) }
|
27
|
+
|
28
|
+
define_method :const do
|
29
|
+
recursive_const_get(const_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
define_method :parent_const do
|
33
|
+
recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
|
34
|
+
end
|
35
|
+
|
36
|
+
define_method :last_const_part do
|
37
|
+
const_name.split('::').last
|
38
|
+
end
|
39
|
+
|
40
|
+
def change_const_value_to(value)
|
41
|
+
parent_const.send(:remove_const, last_const_part)
|
42
|
+
parent_const.const_set(last_const_part, value)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'allows it to be stubbed' do
|
46
|
+
const.should_not eq(7)
|
47
|
+
stub_const(const_name, 7)
|
48
|
+
const.should eq(7)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'resets it to its original value when rspec clears its mocks' do
|
52
|
+
original_value = const
|
53
|
+
original_value.should_not eq(:a)
|
54
|
+
stub_const(const_name, :a)
|
55
|
+
reset_rspec_mocks
|
56
|
+
const.should be(original_value)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'returns the stubbed value' do
|
60
|
+
orig_value = const
|
61
|
+
stub_const(const_name, 7).should eq(7)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
shared_examples_for "unloaded constant stubbing" do |const_name|
|
66
|
+
before { recursive_const_defined?(const_name).should be_false }
|
67
|
+
|
68
|
+
define_method :const do
|
69
|
+
recursive_const_get(const_name)
|
70
|
+
end
|
71
|
+
|
72
|
+
define_method :parent_const do
|
73
|
+
recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
|
74
|
+
end
|
75
|
+
|
76
|
+
define_method :last_const_part do
|
77
|
+
const_name.split('::').last
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'allows it to be stubbed' do
|
81
|
+
stub_const(const_name, 7)
|
82
|
+
const.should eq(7)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'removes the constant when rspec clears its mocks' do
|
86
|
+
stub_const(const_name, 7)
|
87
|
+
reset_rspec_mocks
|
88
|
+
recursive_const_defined?(const_name).should be_false
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'returns the stubbed value' do
|
92
|
+
stub_const(const_name, 7).should eq(7)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'ignores the :transfer_nested_constants option if passed' do
|
96
|
+
stub = Module.new
|
97
|
+
stub_const(const_name, stub, :transfer_nested_constants => true)
|
98
|
+
stub.constants.should eq([])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#stub_const" do
|
103
|
+
context 'for a loaded unnested constant' do
|
104
|
+
it_behaves_like "loaded constant stubbing", "TestClass"
|
105
|
+
|
106
|
+
it 'can be stubbed multiple times but still restores the original value properly' do
|
107
|
+
orig_value = TestClass
|
108
|
+
stub1, stub2 = Module.new, Module.new
|
109
|
+
stub_const("TestClass", stub1)
|
110
|
+
stub_const("TestClass", stub2)
|
111
|
+
|
112
|
+
reset_rspec_mocks
|
113
|
+
TestClass.should be(orig_value)
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'allows nested constants to be transferred to a stub module' do
|
117
|
+
tc_nested = TestClass::Nested
|
118
|
+
stub = Module.new
|
119
|
+
stub_const("TestClass", stub, :transfer_nested_constants => true)
|
120
|
+
stub::M.should eq(:m)
|
121
|
+
stub::N.should eq(:n)
|
122
|
+
stub::Nested.should be(tc_nested)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'allows nested constants to be selectively transferred to a stub module' do
|
126
|
+
stub = Module.new
|
127
|
+
stub_const("TestClass", stub, :transfer_nested_constants => [:M, :N])
|
128
|
+
stub::M.should eq(:m)
|
129
|
+
stub::N.should eq(:n)
|
130
|
+
defined?(stub::Nested).should be_false
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'raises an error if asked to transfer nested constants but given an object that does not support them' do
|
134
|
+
original_tc = TestClass
|
135
|
+
stub = Object.new
|
136
|
+
expect {
|
137
|
+
stub_const("TestClass", stub, :transfer_nested_constants => true)
|
138
|
+
}.to raise_error(ArgumentError)
|
139
|
+
|
140
|
+
TestClass.should be(original_tc)
|
141
|
+
|
142
|
+
expect {
|
143
|
+
stub_const("TestClass", stub, :transfer_nested_constants => [:M])
|
144
|
+
}.to raise_error(ArgumentError)
|
145
|
+
|
146
|
+
TestClass.should be(original_tc)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'raises an error if asked to transfer nested constants on a constant that does not support nested constants' do
|
150
|
+
stub = Module.new
|
151
|
+
expect {
|
152
|
+
stub_const("TOP_LEVEL_VALUE_CONST", stub, :transfer_nested_constants => true)
|
153
|
+
}.to raise_error(ArgumentError)
|
154
|
+
|
155
|
+
TOP_LEVEL_VALUE_CONST.should eq(7)
|
156
|
+
|
157
|
+
expect {
|
158
|
+
stub_const("TOP_LEVEL_VALUE_CONST", stub, :transfer_nested_constants => [:M])
|
159
|
+
}.to raise_error(ArgumentError)
|
160
|
+
|
161
|
+
TOP_LEVEL_VALUE_CONST.should eq(7)
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'raises an error if asked to transfer a nested constant that is not defined' do
|
165
|
+
original_tc = TestClass
|
166
|
+
defined?(TestClass::V).should be_false
|
167
|
+
stub = Module.new
|
168
|
+
|
169
|
+
expect {
|
170
|
+
stub_const("TestClass", stub, :transfer_nested_constants => [:V])
|
171
|
+
}.to raise_error(/cannot transfer nested constant.*V/i)
|
172
|
+
|
173
|
+
TestClass.should be(original_tc)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'for a loaded nested constant' do
|
178
|
+
it_behaves_like "loaded constant stubbing", "TestClass::Nested"
|
179
|
+
end
|
180
|
+
|
181
|
+
context 'for a loaded deeply nested constant' do
|
182
|
+
it_behaves_like "loaded constant stubbing", "TestClass::Nested::NestedEvenMore"
|
183
|
+
end
|
184
|
+
|
185
|
+
context 'for an unloaded unnested constant' do
|
186
|
+
it_behaves_like "unloaded constant stubbing", "X"
|
187
|
+
end
|
188
|
+
|
189
|
+
context 'for an unloaded nested constant' do
|
190
|
+
it_behaves_like "unloaded constant stubbing", "X::Y"
|
191
|
+
|
192
|
+
it 'removes the root constant when rspec clears its mocks' do
|
193
|
+
defined?(X).should be_false
|
194
|
+
stub_const("X::Y", 7)
|
195
|
+
reset_rspec_mocks
|
196
|
+
defined?(X).should be_false
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context 'for an unloaded deeply nested constant' do
|
201
|
+
it_behaves_like "unloaded constant stubbing", "X::Y::Z"
|
202
|
+
|
203
|
+
it 'removes the root constant when rspec clears its mocks' do
|
204
|
+
defined?(X).should be_false
|
205
|
+
stub_const("X::Y::Z", 7)
|
206
|
+
reset_rspec_mocks
|
207
|
+
defined?(X).should be_false
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context 'for an unloaded constant nested within a loaded constant' do
|
212
|
+
it_behaves_like "unloaded constant stubbing", "TestClass::X"
|
213
|
+
|
214
|
+
it 'removes the unloaded constant but leaves the loaded constant when rspec resets its mocks' do
|
215
|
+
defined?(TestClass).should be_true
|
216
|
+
defined?(TestClass::X).should be_false
|
217
|
+
stub_const("TestClass::X", 7)
|
218
|
+
reset_rspec_mocks
|
219
|
+
defined?(TestClass).should be_true
|
220
|
+
defined?(TestClass::X).should be_false
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'raises a helpful error if it cannot be stubbed due to an intermediary constant that is not a module' do
|
224
|
+
TestClass::M.should be_a(Symbol)
|
225
|
+
expect { stub_const("TestClass::M::X", 5) }.to raise_error(/cannot stub/i)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context 'for an unloaded constant nested deeply within a deeply nested loaded constant' do
|
230
|
+
it_behaves_like "unloaded constant stubbing", "TestClass::Nested::NestedEvenMore::X::Y::Z"
|
231
|
+
|
232
|
+
it 'removes the first unloaded constant but leaves the loaded nested constant when rspec resets its mocks' do
|
233
|
+
defined?(TestClass::Nested::NestedEvenMore).should be_true
|
234
|
+
defined?(TestClass::Nested::NestedEvenMore::X).should be_false
|
235
|
+
stub_const("TestClass::Nested::NestedEvenMore::X::Y::Z", 7)
|
236
|
+
reset_rspec_mocks
|
237
|
+
defined?(TestClass::Nested::NestedEvenMore).should be_true
|
238
|
+
defined?(TestClass::Nested::NestedEvenMore::X).should be_false
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe Constant do
|
245
|
+
describe ".original" do
|
246
|
+
context 'for a previously defined unstubbed constant' do
|
247
|
+
let(:const) { Constant.original("TestClass::M") }
|
248
|
+
|
249
|
+
it("exposes its name") { const.name.should eq("TestClass::M") }
|
250
|
+
it("indicates it was previously defined") { const.should be_previously_defined }
|
251
|
+
it("indicates it has not been stubbed") { const.should_not be_stubbed }
|
252
|
+
it("exposes its original value") { const.original_value.should eq(:m) }
|
253
|
+
end
|
254
|
+
|
255
|
+
context 'for a previously defined stubbed constant' do
|
256
|
+
before { stub_const("TestClass::M", :other) }
|
257
|
+
let(:const) { Constant.original("TestClass::M") }
|
258
|
+
|
259
|
+
it("exposes its name") { const.name.should eq("TestClass::M") }
|
260
|
+
it("indicates it was previously defined") { const.should be_previously_defined }
|
261
|
+
it("indicates it has been stubbed") { const.should be_stubbed }
|
262
|
+
it("exposes its original value") { const.original_value.should eq(:m) }
|
263
|
+
end
|
264
|
+
|
265
|
+
context 'for a previously undefined stubbed constant' do
|
266
|
+
before { stub_const("TestClass::Undefined", :other) }
|
267
|
+
let(:const) { Constant.original("TestClass::Undefined") }
|
268
|
+
|
269
|
+
it("exposes its name") { const.name.should eq("TestClass::Undefined") }
|
270
|
+
it("indicates it was not previously defined") { const.should_not be_previously_defined }
|
271
|
+
it("indicates it has been stubbed") { const.should be_stubbed }
|
272
|
+
it("returns nil for the original value") { const.original_value.should be_nil }
|
273
|
+
end
|
274
|
+
|
275
|
+
context 'for a previously undefined unstubbed constant' do
|
276
|
+
let(:const) { Constant.original("TestClass::Undefined") }
|
277
|
+
|
278
|
+
it("exposes its name") { const.name.should eq("TestClass::Undefined") }
|
279
|
+
it("indicates it was not previously defined") { const.should_not be_previously_defined }
|
280
|
+
it("indicates it has not been stubbed") { const.should_not be_stubbed }
|
281
|
+
it("returns nil for the original value") { const.original_value.should be_nil }
|
282
|
+
end
|
283
|
+
|
284
|
+
context 'for a previously defined constant that has been stubbed twice' do
|
285
|
+
before { stub_const("TestClass::M", 1) }
|
286
|
+
before { stub_const("TestClass::M", 2) }
|
287
|
+
let(:const) { Constant.original("TestClass::M") }
|
288
|
+
|
289
|
+
it("exposes its name") { const.name.should eq("TestClass::M") }
|
290
|
+
it("indicates it was previously defined") { const.should be_previously_defined }
|
291
|
+
it("indicates it has been stubbed") { const.should be_stubbed }
|
292
|
+
it("exposes its original value") { const.original_value.should eq(:m) }
|
293
|
+
end
|
294
|
+
|
295
|
+
context 'for a previously undefined constant that has been stubbed twice' do
|
296
|
+
before { stub_const("TestClass::Undefined", 1) }
|
297
|
+
before { stub_const("TestClass::Undefined", 2) }
|
298
|
+
let(:const) { Constant.original("TestClass::Undefined") }
|
299
|
+
|
300
|
+
it("exposes its name") { const.name.should eq("TestClass::Undefined") }
|
301
|
+
it("indicates it was not previously defined") { const.should_not be_previously_defined }
|
302
|
+
it("indicates it has been stubbed") { const.should be_stubbed }
|
303
|
+
it("returns nil for the original value") { const.original_value.should be_nil }
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|