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.
Files changed (36) hide show
  1. data/Changelog.md +26 -1
  2. data/README.md +14 -2
  3. data/features/stubbing_constants/README.md +62 -0
  4. data/features/stubbing_constants/stub_defined_constant.feature +79 -0
  5. data/features/stubbing_constants/stub_undefined_constant.feature +50 -0
  6. data/lib/rspec/mocks/any_instance.rb +37 -1
  7. data/lib/rspec/mocks/any_instance/chain.rb +0 -81
  8. data/lib/rspec/mocks/any_instance/expectation_chain.rb +57 -0
  9. data/lib/rspec/mocks/any_instance/recorder.rb +6 -1
  10. data/lib/rspec/mocks/any_instance/stub_chain.rb +37 -0
  11. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +25 -0
  12. data/lib/rspec/mocks/argument_list_matcher.rb +93 -0
  13. data/lib/rspec/mocks/argument_matchers.rb +39 -31
  14. data/lib/rspec/mocks/error_generator.rb +7 -0
  15. data/lib/rspec/mocks/example_methods.rb +41 -0
  16. data/lib/rspec/mocks/framework.rb +2 -1
  17. data/lib/rspec/mocks/message_expectation.rb +21 -13
  18. data/lib/rspec/mocks/methods.rb +4 -0
  19. data/lib/rspec/mocks/proxy.rb +10 -4
  20. data/lib/rspec/mocks/stub_const.rb +280 -0
  21. data/lib/rspec/mocks/test_double.rb +3 -2
  22. data/lib/rspec/mocks/version.rb +1 -1
  23. data/spec/rspec/mocks/any_instance/message_chains_spec.rb +1 -1
  24. data/spec/rspec/mocks/any_instance_spec.rb +66 -26
  25. data/spec/rspec/mocks/argument_expectation_spec.rb +7 -7
  26. data/spec/rspec/mocks/at_least_spec.rb +14 -0
  27. data/spec/rspec/mocks/block_return_value_spec.rb +8 -0
  28. data/spec/rspec/mocks/mock_spec.rb +33 -20
  29. data/spec/rspec/mocks/multiple_return_value_spec.rb +2 -2
  30. data/spec/rspec/mocks/null_object_mock_spec.rb +22 -0
  31. data/spec/rspec/mocks/stub_chain_spec.rb +45 -45
  32. data/spec/rspec/mocks/stub_const_spec.rb +309 -0
  33. data/spec/rspec/mocks/stub_spec.rb +2 -2
  34. data/spec/spec_helper.rb +0 -40
  35. metadata +18 -6
  36. 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 ArgumentExpectation do
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
- argument_expecatation = RSpec::Mocks::ArgumentExpectation.new
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
- argument_expecatation.is_matcher?(obj).should be_true
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
- argument_expecatation = RSpec::Mocks::ArgumentExpectation.new
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
- argument_expecatation.is_matcher?(obj).should be_true
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
- argument_expecatation = RSpec::Mocks::ArgumentExpectation.new
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
- argument_expecatation.is_matcher?(obj).should be_false
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
- it "passes proc to expectation block without an argument", :ruby => '> 1.8.6' do
180
- # We eval this because Ruby 1.8.6's syntax parser barfs on { |&block| ... }
181
- # and prevents the entire spec suite from running.
182
- eval("@double.should_receive(:foo) {|&block| block.call.should eq(:bar)}")
183
- @double.foo { :bar }
184
- end
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
- it "passes proc to expectation block with an argument", :ruby => '> 1.8.6' do
187
- eval("@double.should_receive(:foo) {|arg, &block| block.call.should eq(:bar)}")
188
- @double.foo(:arg) { :bar }
189
- end
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
- it "passes proc to stub block without an argurment", :ruby => '>1.8.6' do
192
- eval("@double.stub(:foo) {|&block| block.call.should eq(:bar)}")
193
- @double.foo { :bar }
194
- end
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
- it "passes proc to stub block with an argument", :ruby => '> 1.8.6' do
197
- eval("@double.stub(:foo) {|arg, &block| block.call.should eq(:bar)}")
198
- @double.foo(:arg) { :bar }
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(RuntimeError)
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 stub return value for subsequent calls" do
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(:stub_result)
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
- subject { Object.new }
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
- subject.stub_chain(:msg1).and_return(:return_value)
12
- subject.msg1.should equal(:return_value)
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
- subject.stub_chain(:msg1) { :return_value }
19
- subject.msg1.should equal(:return_value)
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
- subject.stub_chain(:msg1 => :return_value)
26
- subject.msg1.should equal(:return_value)
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
- subject.stub_chain(:msg1, :msg2).and_return(:return_value)
35
- subject.msg1.msg2.should equal(:return_value)
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
- subject.stub_chain(:msg1, :msg2) { :return_value }
42
- subject.msg1.msg2.should equal(:return_value)
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
- subject.stub_chain(:msg1, :msg2 => :return_value)
49
- subject.msg1.msg2.should equal(:return_value)
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
- subject.stub_chain(:msg1, :msg2, :msg3, :msg4).and_return(:return_value)
58
- subject.msg1.msg2.msg3.msg4.should equal(:return_value)
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
- subject.stub_chain(:msg1, :msg2, :msg3, :msg4) { :return_value }
65
- subject.msg1.msg2.msg3.msg4.should equal(:return_value)
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
- subject.stub_chain(:msg1, :msg2, :msg3, :msg4 => :return_value)
72
- subject.msg1.msg2.msg3.msg4.should equal(:return_value)
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
- subject.stub_chain("msg1.msg2.msg3.msg4" => :return_value)
79
- subject.msg1.msg2.msg3.msg4.should equal(:return_value)
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
- subject.stub_chain(:msg1, :msg2, :msg3, :msg4).and_return(:return_value)
86
- subject.msg1.msg2.msg3.msg4.should equal(:return_value)
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
- subject.stub_chain(:msg1, :msg2, :msg3).and_return(:first)
94
- subject.stub_chain(:msg1, :msg2, :msg4).and_return(:second)
93
+ object.stub_chain(:msg1, :msg2, :msg3).and_return(:first)
94
+ object.stub_chain(:msg1, :msg2, :msg4).and_return(:second)
95
95
 
96
- subject.msg1.msg2.msg3.should equal(:first)
97
- subject.msg1.msg2.msg4.should equal(:second)
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
- subject.stub_chain(:msg1, :msg2, :msg3).and_return(:first)
104
- subject.stub_chain(:msg4, :msg2, :msg3).and_return(:second)
103
+ object.stub_chain(:msg1, :msg2, :msg3).and_return(:first)
104
+ object.stub_chain(:msg4, :msg2, :msg3).and_return(:second)
105
105
 
106
- subject.msg1.msg2.msg3.should equal(:first)
107
- subject.msg4.msg2.msg3.should equal(:second)
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
- subject.stub_chain(:msg1, :msg2, :msg3 => :first)
116
- subject.stub_chain(:msg1, :msg2, :msg4 => :second)
115
+ object.stub_chain(:msg1, :msg2, :msg3 => :first)
116
+ object.stub_chain(:msg1, :msg2, :msg4 => :second)
117
117
 
118
- subject.msg1.msg2.msg3.should equal(:first)
119
- subject.msg1.msg2.msg4.should equal(:second)
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
- subject.stub_chain(:msg1, :msg2, :msg3 => :first)
126
- subject.stub_chain(:msg4, :msg2, :msg3 => :second)
125
+ object.stub_chain(:msg1, :msg2, :msg3 => :first)
126
+ object.stub_chain(:msg4, :msg2, :msg3 => :second)
127
127
 
128
- subject.msg1.msg2.msg3.should equal(:first)
129
- subject.msg4.msg2.msg3.should equal(:second)
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
- subject.stub_chain("msg1.msg2.msg3").and_return(:return_value)
137
- subject.msg1.msg2.msg3.should equal(:return_value)
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
- subject.stub_chain(:msg1, :msg2, :msg3, :msg4).and_return(:first)
142
- subject.stub_chain(:msg1, :msg2, :msg3, :msg5).and_return(:second)
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
- subject.msg1.msg2.msg3.msg4.should equal(:first)
145
- subject.msg1.msg2.msg3.msg5.should equal(:second)
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
+