spy 0.0.1 → 0.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.
@@ -16,466 +16,506 @@ class TestSubClass < TestClass
16
16
  P = :p
17
17
  end
18
18
 
19
- module RSpec
20
- module Mocks
21
- describe "Constant Mutating", :broken do
22
- include RSpec::Mocks::RecursiveConstMethods
19
+ require "rspec/mocks/mutate_const"
23
20
 
24
- def reset_rspec_mocks
25
- ::RSpec::Mocks.space.reset_all
21
+ module Spy
22
+ describe "Constant Mutating" do
23
+
24
+ require "rspec/mocks/mutate_const"
25
+ include RSpec::Mocks::RecursiveConstMethods
26
+
27
+ def reset_rspec_mocks
28
+ Spy.teardown
29
+ end
30
+
31
+ def on_const(const_name)
32
+ parent_const = recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
33
+ Spy.on_const(parent_const, const_name.split('::').last.to_sym)
34
+ end
35
+
36
+ def stub_const(const_name, value)
37
+ on_const(const_name).and_return(value)
38
+ value
39
+ end
40
+
41
+ def hide_const(const_name)
42
+ on_const(const_name).and_hide
43
+ end
44
+
45
+ shared_context "constant example methods" do |const_name|
46
+ define_method :const do
47
+ recursive_const_get(const_name)
26
48
  end
27
49
 
28
- shared_context "constant example methods" do |const_name|
29
- define_method :const do
30
- recursive_const_get(const_name)
31
- end
50
+ define_method :parent_const do
51
+ recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
52
+ end
32
53
 
33
- define_method :parent_const do
34
- recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
35
- end
54
+ define_method :last_const_part do
55
+ const_name.split('::').last
56
+ end
57
+ end
36
58
 
37
- define_method :last_const_part do
38
- const_name.split('::').last
39
- end
59
+ shared_examples_for "loaded constant stubbing" do |const_name|
60
+ include_context "constant example methods", const_name
61
+
62
+ let!(:original_const_value) { const }
63
+ after { change_const_value_to(original_const_value) }
64
+
65
+ def change_const_value_to(value)
66
+ parent_const.send(:remove_const, last_const_part)
67
+ parent_const.const_set(last_const_part, value)
40
68
  end
41
69
 
42
- shared_examples_for "loaded constant stubbing" do |const_name|
43
- include_context "constant example methods", const_name
70
+ it 'allows it to be stubbed' do
71
+ expect(const).not_to eq(7)
72
+ stub_const(const_name, 7)
73
+ expect(const).to eq(7)
74
+ end
44
75
 
45
- let!(:original_const_value) { const }
46
- after { change_const_value_to(original_const_value) }
76
+ it 'resets it to its original value when rspec clears its mocks' do
77
+ original_value = const
78
+ expect(original_value).not_to eq(:a)
79
+ stub_const(const_name, :a)
80
+ reset_rspec_mocks
81
+ expect(const).to be(original_value)
82
+ end
47
83
 
48
- def change_const_value_to(value)
49
- parent_const.send(:remove_const, last_const_part)
50
- parent_const.const_set(last_const_part, value)
51
- end
84
+ it 'returns the stubbed value' do
85
+ expect(stub_const(const_name, 7)).to eq(7)
86
+ end
87
+ end
52
88
 
53
- it 'allows it to be stubbed' do
54
- expect(const).not_to eq(7)
55
- stub_const(const_name, 7)
56
- expect(const).to eq(7)
57
- end
89
+ shared_examples_for "loaded constant hiding" do |const_name|
90
+ before do
91
+ expect(recursive_const_defined?(const_name)).to be_true
92
+ end
58
93
 
59
- it 'resets it to its original value when rspec clears its mocks' do
60
- original_value = const
61
- expect(original_value).not_to eq(:a)
62
- stub_const(const_name, :a)
63
- reset_rspec_mocks
64
- expect(const).to be(original_value)
65
- end
94
+ it 'allows it to be hidden' do
95
+ hide_const(const_name)
96
+ expect(recursive_const_defined?(const_name)).to be_false
97
+ end
66
98
 
67
- it 'returns the stubbed value' do
68
- expect(stub_const(const_name, 7)).to eq(7)
69
- end
99
+ it 'resets the constant when rspec clear its mocks' do
100
+ hide_const(const_name)
101
+ reset_rspec_mocks
102
+ expect(recursive_const_defined?(const_name)).to be_true
70
103
  end
71
104
 
72
- shared_examples_for "loaded constant hiding" do |const_name|
73
- before do
74
- expect(recursive_const_defined?(const_name)).to be_true
75
- end
105
+ it 'returns nil' do
106
+ expect(hide_const(const_name)).to be_nil
107
+ end
108
+ end
76
109
 
77
- it 'allows it to be hidden' do
78
- hide_const(const_name)
79
- expect(recursive_const_defined?(const_name)).to be_false
80
- end
110
+ shared_examples_for "unloaded constant stubbing" do |const_name|
111
+ include_context "constant example methods", const_name
81
112
 
82
- it 'resets the constant when rspec clear its mocks' do
83
- hide_const(const_name)
84
- reset_rspec_mocks
85
- expect(recursive_const_defined?(const_name)).to be_true
86
- end
113
+ before do
114
+ expect(recursive_const_defined?(const_name)).to be_false
115
+ end
87
116
 
88
- it 'returns nil' do
89
- expect(hide_const(const_name)).to be_nil
90
- end
117
+ it 'allows it to be stubbed' do
118
+ stub_const(const_name, 7)
119
+ expect(const).to eq(7)
91
120
  end
92
121
 
93
- shared_examples_for "unloaded constant stubbing" do |const_name|
94
- include_context "constant example methods", const_name
122
+ it 'removes the constant when rspec clears its mocks' do
123
+ stub_const(const_name, 7)
124
+ reset_rspec_mocks
125
+ expect(recursive_const_defined?(const_name)).to be_false
126
+ end
95
127
 
96
- before do
97
- expect(recursive_const_defined?(const_name)).to be_false
98
- end
128
+ it 'returns the stubbed value' do
129
+ expect(stub_const(const_name, 7)).to eq(7)
130
+ end
99
131
 
100
- it 'allows it to be stubbed' do
101
- stub_const(const_name, 7)
102
- expect(const).to eq(7)
103
- end
132
+ it 'ignores the :transfer_nested_constants option if passed' do
133
+ stub = Module.new
134
+ stub_const(const_name, stub, :transfer_nested_constants => true)
135
+ expect(stub.constants).to eq([])
136
+ end
137
+ end
104
138
 
105
- it 'removes the constant when rspec clears its mocks' do
106
- stub_const(const_name, 7)
107
- reset_rspec_mocks
108
- expect(recursive_const_defined?(const_name)).to be_false
109
- end
139
+ shared_examples_for "unloaded constant hiding" do |const_name|
140
+ include_context "constant example methods", const_name
110
141
 
111
- it 'returns the stubbed value' do
112
- expect(stub_const(const_name, 7)).to eq(7)
113
- end
142
+ before do
143
+ expect(recursive_const_defined?(const_name)).to be_false
144
+ end
114
145
 
115
- it 'ignores the :transfer_nested_constants option if passed' do
116
- stub = Module.new
117
- stub_const(const_name, stub, :transfer_nested_constants => true)
118
- expect(stub.constants).to eq([])
119
- end
146
+ it 'allows it to be hidden, though the operation has no effect' do
147
+ hide_const(const_name)
148
+ expect(recursive_const_defined?(const_name)).to be_false
120
149
  end
121
150
 
122
- shared_examples_for "unloaded constant hiding" do |const_name|
123
- include_context "constant example methods", const_name
151
+ it 'remains undefined after rspec clears its mocks' do
152
+ hide_const(const_name)
153
+ reset_rspec_mocks
154
+ expect(recursive_const_defined?(const_name)).to be_false
155
+ end
124
156
 
125
- before do
126
- expect(recursive_const_defined?(const_name)).to be_false
127
- end
157
+ it 'returns nil' do
158
+ expect(hide_const(const_name)).to be_nil
159
+ end
160
+ end
128
161
 
129
- it 'allows it to be hidden, though the operation has no effect' do
130
- hide_const(const_name)
131
- expect(recursive_const_defined?(const_name)).to be_false
132
- end
162
+ describe "#hide_const" do
163
+ context 'for a loaded nested constant' do
164
+ it_behaves_like "loaded constant hiding", "TestClass::Nested"
165
+ end
133
166
 
134
- it 'remains undefined after rspec clears its mocks' do
135
- hide_const(const_name)
136
- reset_rspec_mocks
137
- expect(recursive_const_defined?(const_name)).to be_false
167
+ context 'for a loaded constant prefixed with ::' do
168
+ it_behaves_like 'loaded constant hiding', "::TestClass"
169
+ end
170
+
171
+ context 'for an unloaded constant with nested name that matches a top-level constant' do
172
+ it_behaves_like "unloaded constant hiding", "TestClass::Hash"
173
+
174
+ it 'does not hide the top-level constant' do
175
+ top_level_hash = ::Hash
176
+
177
+ hide_const("TestClass::Hash")
178
+ expect(::Hash).to equal(top_level_hash)
138
179
  end
139
180
 
140
- it 'returns nil' do
141
- expect(hide_const(const_name)).to be_nil
181
+ it 'does not affect the ability to access the top-level constant from nested contexts', :silence_warnings do
182
+ top_level_hash = ::Hash
183
+
184
+ hide_const("TestClass::Hash")
185
+ expect(TestClass::Hash).to equal(top_level_hash)
142
186
  end
143
187
  end
144
188
 
145
- describe "#hide_const" do
146
- context 'for a loaded nested constant' do
147
- it_behaves_like "loaded constant hiding", "TestClass::Nested"
148
- end
189
+ context 'for a loaded deeply nested constant' do
190
+ it_behaves_like "loaded constant hiding", "TestClass::Nested::NestedEvenMore"
191
+ end
149
192
 
150
- context 'for a loaded constant prefixed with ::' do
151
- it_behaves_like 'loaded constant hiding', "::TestClass"
152
- end
193
+ context 'for an unloaded unnested constant' do
194
+ it_behaves_like "unloaded constant hiding", "X"
195
+ end
153
196
 
154
- context 'for an unloaded constant with nested name that matches a top-level constant' do
155
- it_behaves_like "unloaded constant hiding", "TestClass::Hash"
197
+ context 'for an unloaded nested constant' do
198
+ it_behaves_like "unloaded constant hiding", "X::Y"
199
+ end
156
200
 
157
- it 'does not hide the top-level constant' do
158
- top_level_hash = ::Hash
201
+ it 'can be hidden multiple times but still restores the original value properly' do
202
+ orig_value = TestClass
203
+ hide_const("TestClass")
204
+ hide_const("TestClass")
159
205
 
160
- hide_const("TestClass::Hash")
161
- expect(::Hash).to equal(top_level_hash)
162
- end
206
+ reset_rspec_mocks
207
+ expect(TestClass).to be(orig_value)
208
+ end
163
209
 
164
- it 'does not affect the ability to access the top-level constant from nested contexts', :silence_warnings do
165
- top_level_hash = ::Hash
210
+ it 'allows a constant to be hidden, then stubbed, restoring it to its original value properly' do
211
+ orig_value = TOP_LEVEL_VALUE_CONST
166
212
 
167
- hide_const("TestClass::Hash")
168
- expect(TestClass::Hash).to equal(top_level_hash)
169
- end
170
- end
213
+ hide_const("TOP_LEVEL_VALUE_CONST")
214
+ expect(recursive_const_defined?("TOP_LEVEL_VALUE_CONST")).to be_false
171
215
 
172
- context 'for a loaded deeply nested constant' do
173
- it_behaves_like "loaded constant hiding", "TestClass::Nested::NestedEvenMore"
174
- end
216
+ stub_const("TOP_LEVEL_VALUE_CONST", 12345)
217
+ expect(TOP_LEVEL_VALUE_CONST).to eq 12345
175
218
 
176
- context 'for an unloaded unnested constant' do
177
- it_behaves_like "unloaded constant hiding", "X"
178
- end
219
+ reset_rspec_mocks
220
+ expect(TOP_LEVEL_VALUE_CONST).to eq orig_value
221
+ end
222
+ end
179
223
 
180
- context 'for an unloaded nested constant' do
181
- it_behaves_like "unloaded constant hiding", "X::Y"
182
- end
224
+ describe "#stub_const" do
225
+ context 'for a loaded unnested constant' do
226
+ it_behaves_like "loaded constant stubbing", "TestClass"
183
227
 
184
- it 'can be hidden multiple times but still restores the original value properly' do
228
+ it 'can be stubbed multiple times but still restores the original value properly' do
185
229
  orig_value = TestClass
186
- hide_const("TestClass")
187
- hide_const("TestClass")
230
+ stub1, stub2 = Module.new, Module.new
231
+ stub_const("TestClass", stub1)
232
+ stub_const("TestClass", stub2)
188
233
 
189
234
  reset_rspec_mocks
190
235
  expect(TestClass).to be(orig_value)
191
236
  end
192
237
 
193
- it 'allows a constant to be hidden, then stubbed, restoring it to its original value properly' do
194
- orig_value = TOP_LEVEL_VALUE_CONST
238
+ it 'allows nested constants to be transferred to a stub module' do
239
+ tc_nested = TestClass::Nested
240
+ stub = Module.new
241
+ stub_const("TestClass", stub, :transfer_nested_constants => true)
242
+ expect(stub::M).to eq(:m)
243
+ expect(stub::N).to eq(:n)
244
+ expect(stub::Nested).to be(tc_nested)
245
+ end
195
246
 
196
- hide_const("TOP_LEVEL_VALUE_CONST")
197
- expect(recursive_const_defined?("TOP_LEVEL_VALUE_CONST")).to be_false
247
+ it 'does not transfer nested constants that are inherited from a superclass' do
248
+ stub = Module.new
249
+ stub_const("TestSubClass", stub, :transfer_nested_constants => true)
250
+ expect(stub::P).to eq(:p)
251
+ expect(defined?(stub::M)).to be_false
252
+ expect(defined?(stub::N)).to be_false
253
+ end
198
254
 
199
- stub_const("TOP_LEVEL_VALUE_CONST", 12345)
200
- expect(TOP_LEVEL_VALUE_CONST).to eq 12345
255
+ it 'raises an error when asked to transfer a nested inherited constant' do
256
+ original_tsc = TestSubClass
201
257
 
202
- reset_rspec_mocks
203
- expect(TOP_LEVEL_VALUE_CONST).to eq orig_value
258
+ expect {
259
+ stub_const("TestSubClass", Module.new, :transfer_nested_constants => [:M])
260
+ }.to raise_error(ArgumentError)
261
+
262
+ expect(TestSubClass).to be(original_tsc)
263
+ end
264
+
265
+ it 'allows nested constants to be selectively transferred to a stub module' do
266
+ stub = Module.new
267
+ stub_const("TestClass", stub, :transfer_nested_constants => [:M, :N])
268
+ expect(stub::M).to eq(:m)
269
+ expect(stub::N).to eq(:n)
270
+ expect(defined?(stub::Nested)).to be_false
204
271
  end
205
- end
206
272
 
207
- describe "#stub_const" do
208
- context 'for a loaded unnested constant' do
209
- it_behaves_like "loaded constant stubbing", "TestClass"
273
+ it 'raises an error if asked to transfer nested constants but given an object that does not support them' do
274
+ original_tc = TestClass
275
+ stub = Object.new
276
+ expect {
277
+ stub_const("TestClass", stub, :transfer_nested_constants => true)
278
+ }.to raise_error(ArgumentError)
210
279
 
211
- it 'can be stubbed multiple times but still restores the original value properly' do
212
- orig_value = TestClass
213
- stub1, stub2 = Module.new, Module.new
214
- stub_const("TestClass", stub1)
215
- stub_const("TestClass", stub2)
280
+ expect(TestClass).to be(original_tc)
216
281
 
217
- reset_rspec_mocks
218
- expect(TestClass).to be(orig_value)
219
- end
282
+ expect {
283
+ stub_const("TestClass", stub, :transfer_nested_constants => [:M])
284
+ }.to raise_error(ArgumentError)
220
285
 
221
- it 'allows nested constants to be transferred to a stub module' do
222
- tc_nested = TestClass::Nested
223
- stub = Module.new
224
- stub_const("TestClass", stub, :transfer_nested_constants => true)
225
- expect(stub::M).to eq(:m)
226
- expect(stub::N).to eq(:n)
227
- expect(stub::Nested).to be(tc_nested)
228
- end
229
-
230
- it 'does not transfer nested constants that are inherited from a superclass' do
231
- stub = Module.new
232
- stub_const("TestSubClass", stub, :transfer_nested_constants => true)
233
- expect(stub::P).to eq(:p)
234
- expect(defined?(stub::M)).to be_false
235
- expect(defined?(stub::N)).to be_false
236
- end
237
-
238
- it 'raises an error when asked to transfer a nested inherited constant' do
239
- original_tsc = TestSubClass
240
-
241
- expect {
242
- stub_const("TestSubClass", Module.new, :transfer_nested_constants => [:M])
243
- }.to raise_error(ArgumentError)
244
-
245
- expect(TestSubClass).to be(original_tsc)
246
- end
247
-
248
- it 'allows nested constants to be selectively transferred to a stub module' do
249
- stub = Module.new
250
- stub_const("TestClass", stub, :transfer_nested_constants => [:M, :N])
251
- expect(stub::M).to eq(:m)
252
- expect(stub::N).to eq(:n)
253
- expect(defined?(stub::Nested)).to be_false
254
- end
255
-
256
- it 'raises an error if asked to transfer nested constants but given an object that does not support them' do
257
- original_tc = TestClass
258
- stub = Object.new
259
- expect {
260
- stub_const("TestClass", stub, :transfer_nested_constants => true)
261
- }.to raise_error(ArgumentError)
262
-
263
- expect(TestClass).to be(original_tc)
264
-
265
- expect {
266
- stub_const("TestClass", stub, :transfer_nested_constants => [:M])
267
- }.to raise_error(ArgumentError)
268
-
269
- expect(TestClass).to be(original_tc)
270
- end
271
-
272
- it 'raises an error if asked to transfer nested constants on a constant that does not support nested constants' do
273
- stub = Module.new
274
- expect {
275
- stub_const("TOP_LEVEL_VALUE_CONST", stub, :transfer_nested_constants => true)
276
- }.to raise_error(ArgumentError)
277
-
278
- expect(TOP_LEVEL_VALUE_CONST).to eq(7)
279
-
280
- expect {
281
- stub_const("TOP_LEVEL_VALUE_CONST", stub, :transfer_nested_constants => [:M])
282
- }.to raise_error(ArgumentError)
283
-
284
- expect(TOP_LEVEL_VALUE_CONST).to eq(7)
285
- end
286
-
287
- it 'raises an error if asked to transfer a nested constant that is not defined' do
288
- original_tc = TestClass
289
- expect(defined?(TestClass::V)).to be_false
290
- stub = Module.new
291
-
292
- expect {
293
- stub_const("TestClass", stub, :transfer_nested_constants => [:V])
294
- }.to raise_error(/cannot transfer nested constant.*V/i)
295
-
296
- expect(TestClass).to be(original_tc)
297
- end
286
+ expect(TestClass).to be(original_tc)
298
287
  end
299
288
 
300
- context 'for a loaded nested constant' do
301
- it_behaves_like "loaded constant stubbing", "TestClass::Nested"
302
- end
289
+ it 'raises an error if asked to transfer nested constants on a constant that does not support nested constants' do
290
+ stub = Module.new
291
+ expect {
292
+ stub_const("TOP_LEVEL_VALUE_CONST", stub, :transfer_nested_constants => true)
293
+ }.to raise_error(ArgumentError)
303
294
 
304
- context 'for a loaded constant prefixed with ::' do
305
- it_behaves_like 'loaded constant stubbing', "::TestClass"
306
- end
295
+ expect(TOP_LEVEL_VALUE_CONST).to eq(7)
307
296
 
308
- context 'for an unloaded constant prefixed with ::' do
309
- it_behaves_like 'unloaded constant stubbing', "::SomeUndefinedConst"
310
- end
297
+ expect {
298
+ stub_const("TOP_LEVEL_VALUE_CONST", stub, :transfer_nested_constants => [:M])
299
+ }.to raise_error(ArgumentError)
311
300
 
312
- context 'for an unloaded constant with nested name that matches a top-level constant' do
313
- it_behaves_like "unloaded constant stubbing", "TestClass::Hash"
301
+ expect(TOP_LEVEL_VALUE_CONST).to eq(7)
314
302
  end
315
303
 
316
- context 'for a loaded deeply nested constant' do
317
- it_behaves_like "loaded constant stubbing", "TestClass::Nested::NestedEvenMore"
304
+ it 'raises an error if asked to transfer a nested constant that is not defined' do
305
+ original_tc = TestClass
306
+ expect(defined?(TestClass::V)).to be_false
307
+ stub = Module.new
308
+
309
+ expect {
310
+ stub_const("TestClass", stub, :transfer_nested_constants => [:V])
311
+ }.to raise_error(/cannot transfer nested constant.*V/i)
312
+
313
+ expect(TestClass).to be(original_tc)
318
314
  end
315
+ end
316
+
317
+ context 'for a loaded nested constant' do
318
+ it_behaves_like "loaded constant stubbing", "TestClass::Nested"
319
+ end
320
+
321
+ context 'for a loaded constant prefixed with ::' do
322
+ it_behaves_like 'loaded constant stubbing', "::TestClass"
323
+ end
324
+
325
+ context 'for an unloaded constant prefixed with ::' do
326
+ it_behaves_like 'unloaded constant stubbing', "::SomeUndefinedConst"
327
+ end
319
328
 
320
- context 'for an unloaded unnested constant' do
321
- it_behaves_like "unloaded constant stubbing", "X"
329
+ context 'for an unloaded constant with nested name that matches a top-level constant' do
330
+ it_behaves_like "unloaded constant stubbing", "TestClass::Hash"
331
+ end
332
+
333
+ context 'for a loaded deeply nested constant' do
334
+ it_behaves_like "loaded constant stubbing", "TestClass::Nested::NestedEvenMore"
335
+ end
336
+
337
+ context 'for an unloaded unnested constant' do
338
+ it_behaves_like "unloaded constant stubbing", "X"
339
+ end
340
+
341
+ context 'for an unloaded nested constant' do
342
+ it_behaves_like "unloaded constant stubbing", "X::Y"
343
+
344
+ it 'removes the root constant when rspec clears its mocks' do
345
+ expect(defined?(X)).to be_false
346
+ stub_const("X::Y", 7)
347
+ reset_rspec_mocks
348
+ expect(defined?(X)).to be_false
322
349
  end
350
+ end
323
351
 
324
- context 'for an unloaded nested constant' do
325
- it_behaves_like "unloaded constant stubbing", "X::Y"
352
+ context 'for an unloaded deeply nested constant' do
353
+ it_behaves_like "unloaded constant stubbing", "X::Y::Z"
326
354
 
327
- it 'removes the root constant when rspec clears its mocks' do
328
- expect(defined?(X)).to be_false
329
- stub_const("X::Y", 7)
330
- reset_rspec_mocks
331
- expect(defined?(X)).to be_false
332
- end
355
+ it 'removes the root constant when rspec clears its mocks' do
356
+ expect(defined?(X)).to be_false
357
+ stub_const("X::Y::Z", 7)
358
+ reset_rspec_mocks
359
+ expect(defined?(X)).to be_false
333
360
  end
361
+ end
334
362
 
335
- context 'for an unloaded deeply nested constant' do
336
- it_behaves_like "unloaded constant stubbing", "X::Y::Z"
363
+ context 'for an unloaded constant nested within a loaded constant' do
364
+ it_behaves_like "unloaded constant stubbing", "TestClass::X"
337
365
 
338
- it 'removes the root constant when rspec clears its mocks' do
339
- expect(defined?(X)).to be_false
340
- stub_const("X::Y::Z", 7)
341
- reset_rspec_mocks
342
- expect(defined?(X)).to be_false
343
- end
366
+ it 'removes the unloaded constant but leaves the loaded constant when rspec resets its mocks' do
367
+ expect(defined?(TestClass)).to be_true
368
+ expect(defined?(TestClass::X)).to be_false
369
+ stub_const("TestClass::X", 7)
370
+ reset_rspec_mocks
371
+ expect(defined?(TestClass)).to be_true
372
+ expect(defined?(TestClass::X)).to be_false
344
373
  end
345
374
 
346
- context 'for an unloaded constant nested within a loaded constant' do
347
- it_behaves_like "unloaded constant stubbing", "TestClass::X"
348
-
349
- it 'removes the unloaded constant but leaves the loaded constant when rspec resets its mocks' do
350
- expect(defined?(TestClass)).to be_true
351
- expect(defined?(TestClass::X)).to be_false
352
- stub_const("TestClass::X", 7)
353
- reset_rspec_mocks
354
- expect(defined?(TestClass)).to be_true
355
- expect(defined?(TestClass::X)).to be_false
356
- end
357
-
358
- it 'raises a helpful error if it cannot be stubbed due to an intermediary constant that is not a module' do
359
- expect(TestClass::M).to be_a(Symbol)
360
- expect { stub_const("TestClass::M::X", 5) }.to raise_error(/cannot stub/i)
361
- end
375
+ it 'raises a helpful error if it cannot be stubbed due to an intermediary constant that is not a module' do
376
+ expect(TestClass::M).to be_a(Symbol)
377
+ expect { stub_const("TestClass::M::X", 5) }.to raise_error(/cannot stub/i)
362
378
  end
379
+ end
380
+
381
+ context 'for an unloaded constant nested deeply within a deeply nested loaded constant' do
382
+ it_behaves_like "unloaded constant stubbing", "TestClass::Nested::NestedEvenMore::X::Y::Z"
363
383
 
364
- context 'for an unloaded constant nested deeply within a deeply nested loaded constant' do
365
- it_behaves_like "unloaded constant stubbing", "TestClass::Nested::NestedEvenMore::X::Y::Z"
366
-
367
- it 'removes the first unloaded constant but leaves the loaded nested constant when rspec resets its mocks' do
368
- expect(defined?(TestClass::Nested::NestedEvenMore)).to be_true
369
- expect(defined?(TestClass::Nested::NestedEvenMore::X)).to be_false
370
- stub_const("TestClass::Nested::NestedEvenMore::X::Y::Z", 7)
371
- reset_rspec_mocks
372
- expect(defined?(TestClass::Nested::NestedEvenMore)).to be_true
373
- expect(defined?(TestClass::Nested::NestedEvenMore::X)).to be_false
374
- end
384
+ it 'removes the first unloaded constant but leaves the loaded nested constant when rspec resets its mocks' do
385
+ expect(defined?(TestClass::Nested::NestedEvenMore)).to be_true
386
+ expect(defined?(TestClass::Nested::NestedEvenMore::X)).to be_false
387
+ stub_const("TestClass::Nested::NestedEvenMore::X::Y::Z", 7)
388
+ reset_rspec_mocks
389
+ expect(defined?(TestClass::Nested::NestedEvenMore)).to be_true
390
+ expect(defined?(TestClass::Nested::NestedEvenMore::X)).to be_false
375
391
  end
376
392
  end
377
393
  end
394
+ end
378
395
 
379
- describe Constant do
380
- describe ".original" do
381
- context 'for a previously defined unstubbed constant' do
382
- let(:const) { Constant.original("TestClass::M") }
383
-
384
- it("exposes its name") { expect(const.name).to eq("TestClass::M") }
385
- it("indicates it was previously defined") { expect(const).to be_previously_defined }
386
- it("indicates it has not been mutated") { expect(const).not_to be_mutated }
387
- it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
388
- it("indicates it has not been hidden") { expect(const).not_to be_hidden }
389
- it("exposes its original value") { expect(const.original_value).to eq(:m) }
390
- end
396
+ describe Constant do
397
+ require "rspec/mocks/mutate_const"
398
+ include RSpec::Mocks::RecursiveConstMethods
391
399
 
392
- context 'for a previously defined stubbed constant' do
393
- before { stub_const("TestClass::M", :other) }
394
- let(:const) { Constant.original("TestClass::M") }
400
+ def reset_rspec_mocks
401
+ Spy.teardown
402
+ end
395
403
 
396
- it("exposes its name") { expect(const.name).to eq("TestClass::M") }
397
- it("indicates it was previously defined") { expect(const).to be_previously_defined }
398
- it("indicates it has been mutated") { expect(const).to be_mutated }
399
- it("indicates it has been stubbed") { expect(const).to be_stubbed }
400
- it("indicates it has not been hidden") { expect(const).not_to be_hidden }
401
- it("exposes its original value") { expect(const.original_value).to eq(:m) }
402
- end
404
+ def on_const(const_name)
405
+ parent_const = recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
406
+ Spy.on_const(parent_const, const_name.split('::').last.to_sym)
407
+ end
403
408
 
404
- context 'for a previously undefined stubbed constant' do
405
- before { stub_const("TestClass::Undefined", :other) }
406
- let(:const) { Constant.original("TestClass::Undefined") }
409
+ def stub_const(const_name, value)
410
+ on_const(const_name).and_return(value)
411
+ end
407
412
 
408
- it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
409
- it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
410
- it("indicates it has been mutated") { expect(const).to be_mutated }
411
- it("indicates it has been stubbed") { expect(const).to be_stubbed }
412
- it("indicates it has not been hidden") { expect(const).not_to be_hidden }
413
- it("returns nil for the original value") { expect(const.original_value).to be_nil }
414
- end
413
+ def hide_const(const_name)
414
+ on_const(const_name).and_hide
415
+ end
415
416
 
416
- context 'for a previously undefined unstubbed constant' do
417
- let(:const) { Constant.original("TestClass::Undefined") }
417
+ def original(const_name)
418
+ parent_const = recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
419
+ Spy.get_const(parent_const, const_name.split('::').last.to_sym)
420
+ end
418
421
 
419
- it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
420
- it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
421
- it("indicates it has not been mutated") { expect(const).not_to be_mutated }
422
- it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
423
- it("indicates it has not been hidden") { expect(const).not_to be_hidden }
424
- it("returns nil for the original value") { expect(const.original_value).to be_nil }
425
- end
422
+ describe ".original" do
423
+ context 'for a previously defined unstubbed constant' do
424
+ let(:const) { original("TestClass::M") }
426
425
 
427
- context 'for a previously defined constant that has been stubbed twice' do
428
- before { stub_const("TestClass::M", 1) }
429
- before { stub_const("TestClass::M", 2) }
430
- let(:const) { Constant.original("TestClass::M") }
431
-
432
- it("exposes its name") { expect(const.name).to eq("TestClass::M") }
433
- it("indicates it was previously defined") { expect(const).to be_previously_defined }
434
- it("indicates it has been mutated") { expect(const).to be_mutated }
435
- it("indicates it has been stubbed") { expect(const).to be_stubbed }
436
- it("indicates it has not been hidden") { expect(const).not_to be_hidden }
437
- it("exposes its original value") { expect(const.original_value).to eq(:m) }
438
- end
426
+ it("exposes its name") { expect(const.name).to eq("TestClass::M") }
427
+ it("indicates it was previously defined") { expect(const).to be_previously_defined }
428
+ it("indicates it has not been mutated") { expect(const).not_to be_mutated }
429
+ it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
430
+ it("indicates it has not been hidden") { expect(const).not_to be_hidden }
431
+ it("exposes its original value") { expect(const.original_value).to eq(:m) }
432
+ end
439
433
 
440
- context 'for a previously undefined constant that has been stubbed twice' do
441
- before { stub_const("TestClass::Undefined", 1) }
442
- before { stub_const("TestClass::Undefined", 2) }
443
- let(:const) { Constant.original("TestClass::Undefined") }
444
-
445
- it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
446
- it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
447
- it("indicates it has been mutated") { expect(const).to be_mutated }
448
- it("indicates it has been stubbed") { expect(const).to be_stubbed }
449
- it("indicates it has not been hidden") { expect(const).not_to be_hidden }
450
- it("returns nil for the original value") { expect(const.original_value).to be_nil }
451
- end
434
+ context 'for a previously defined stubbed constant' do
435
+ before { stub_const("TestClass::M", :other) }
436
+ let(:const) { original("TestClass::M") }
452
437
 
453
- context 'for a previously defined hidden constant' do
454
- before { hide_const("TestClass::M") }
455
- let(:const) { Constant.original("TestClass::M") }
438
+ it("exposes its name") { expect(const.name).to eq("TestClass::M") }
439
+ it("indicates it was previously defined") { expect(const).to be_previously_defined }
440
+ it("indicates it has been mutated") { expect(const).to be_mutated }
441
+ it("indicates it has been stubbed") { expect(const).to be_stubbed }
442
+ it("indicates it has not been hidden") { expect(const).not_to be_hidden }
443
+ it("exposes its original value") { expect(const.original_value).to eq(:m) }
444
+ end
456
445
 
457
- it("exposes its name") { expect(const.name).to eq("TestClass::M") }
458
- it("indicates it was previously defined") { expect(const).to be_previously_defined }
459
- it("indicates it has been mutated") { expect(const).to be_mutated }
460
- it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
461
- it("indicates it has been hidden") { expect(const).to be_hidden }
462
- it("exposes its original value") { expect(const.original_value).to eq(:m) }
463
- end
446
+ context 'for a previously undefined stubbed constant' do
447
+ before { stub_const("TestClass::Undefined", :other) }
448
+ let(:const) { original("TestClass::Undefined") }
464
449
 
465
- context 'for a previously defined constant that has been hidden twice' do
466
- before { hide_const("TestClass::M") }
467
- before { hide_const("TestClass::M") }
468
- let(:const) { Constant.original("TestClass::M") }
469
-
470
- it("exposes its name") { expect(const.name).to eq("TestClass::M") }
471
- it("indicates it was previously defined") { expect(const).to be_previously_defined }
472
- it("indicates it has been mutated") { expect(const).to be_mutated }
473
- it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
474
- it("indicates it has been hidden") { expect(const).to be_hidden }
475
- it("exposes its original value") { expect(const.original_value).to eq(:m) }
476
- end
450
+ it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
451
+ it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
452
+ it("indicates it has been mutated") { expect(const).to be_mutated }
453
+ it("indicates it has been stubbed") { expect(const).to be_stubbed }
454
+ it("indicates it has not been hidden") { expect(const).not_to be_hidden }
455
+ it("returns nil for the original value") { expect(const.original_value).to be_nil }
456
+ end
457
+
458
+ context 'for a previously undefined unstubbed constant' do
459
+ let(:const) { original("TestClass::Undefined") }
460
+
461
+ it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
462
+ it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
463
+ it("indicates it has not been mutated") { expect(const).not_to be_mutated }
464
+ it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
465
+ it("indicates it has not been hidden") { expect(const).not_to be_hidden }
466
+ it("returns nil for the original value") { expect(const.original_value).to be_nil }
467
+ end
468
+
469
+ context 'for a previously defined constant that has been stubbed twice' do
470
+ before { stub_const("TestClass::M", 1) }
471
+ before { stub_const("TestClass::M", 2) }
472
+ let(:const) { original("TestClass::M") }
473
+
474
+ it("exposes its name") { expect(const.name).to eq("TestClass::M") }
475
+ it("indicates it was previously defined") { expect(const).to be_previously_defined }
476
+ it("indicates it has been mutated") { expect(const).to be_mutated }
477
+ it("indicates it has been stubbed") { expect(const).to be_stubbed }
478
+ it("indicates it has not been hidden") { expect(const).not_to be_hidden }
479
+ it("exposes its original value") { expect(const.original_value).to eq(:m) }
480
+ end
481
+
482
+ context 'for a previously undefined constant that has been stubbed twice' do
483
+ before { stub_const("TestClass::Undefined", 1) }
484
+ before { stub_const("TestClass::Undefined", 2) }
485
+ let(:const) { original("TestClass::Undefined") }
486
+
487
+ it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
488
+ it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
489
+ it("indicates it has been mutated") { expect(const).to be_mutated }
490
+ it("indicates it has been stubbed") { expect(const).to be_stubbed }
491
+ it("indicates it has not been hidden") { expect(const).not_to be_hidden }
492
+ it("returns nil for the original value") { expect(const.original_value).to be_nil }
493
+ end
494
+
495
+ context 'for a previously defined hidden constant' do
496
+ before { hide_const("TestClass::M") }
497
+ let(:const) { original("TestClass::M") }
498
+
499
+ it("exposes its name") { expect(const.name).to eq("TestClass::M") }
500
+ it("indicates it was previously defined") { expect(const).to be_previously_defined }
501
+ it("indicates it has been mutated") { expect(const).to be_mutated }
502
+ it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
503
+ it("indicates it has been hidden") { expect(const).to be_hidden }
504
+ it("exposes its original value") { expect(const.original_value).to eq(:m) }
505
+ end
506
+
507
+ context 'for a previously defined constant that has been hidden twice' do
508
+ before { hide_const("TestClass::M") }
509
+ before { hide_const("TestClass::M") }
510
+ let(:const) { original("TestClass::M") }
511
+
512
+ it("exposes its name") { expect(const.name).to eq("TestClass::M") }
513
+ it("indicates it was previously defined") { expect(const).to be_previously_defined }
514
+ it("indicates it has been mutated") { expect(const).to be_mutated }
515
+ it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
516
+ it("indicates it has been hidden") { expect(const).to be_hidden }
517
+ it("exposes its original value") { expect(const.original_value).to eq(:m) }
477
518
  end
478
519
  end
479
520
  end
480
521
  end
481
-