spy 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-