rspec-fire 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -4,8 +4,11 @@
4
4
  0.2 - 3 September 2011
5
5
  * Checks method arity when a 'with' call is present
6
6
 
7
- 0.2.1 - 14 January 2011
7
+ 0.2.1 - 14 January 2012
8
8
  * rspec 2.8 support
9
9
 
10
- 0.2.2 - 31 January 2011
11
- * Added missing require needed for some code bases.
10
+ 0.2.2 - 31 January 2012
11
+ * Added missing require needed for some code bases.
12
+
13
+ 0.3.0 - 17 March 2012
14
+ * Added support for doubling constants.
data/README.md CHANGED
@@ -63,6 +63,12 @@ Bit of setup in your `spec_helper.rb`:
63
63
 
64
64
  Specify the class being doubled in your specs:
65
65
 
66
+ class User < Struct.new(:notifier)
67
+ def suspend!
68
+ notifier.notify("suspended as")
69
+ end
70
+ end
71
+
66
72
  describe User, '#suspend!' do
67
73
  it 'sends a notification' do
68
74
  # Only this one line differs from how you write specs normally
@@ -97,6 +103,81 @@ the context of your app:
97
103
 
98
104
  rspec -rspec/spec_helper.rb spec/unit/my_spec.rb
99
105
 
106
+ ### Doubling constants
107
+
108
+ A particularly excellent feature. You can stub out constants using
109
+ `fire_replaced_class_double`, removing the need to dependency inject
110
+ collaborators (a technique that can sometimes be cumbersome).
111
+
112
+ class User
113
+ def suspend!
114
+ EmailNotifier.notify("suspended as")
115
+ end
116
+ end
117
+
118
+ describe User, '#suspend!' do
119
+ it 'sends a notification' do
120
+ # Only this one line differs from how you write specs normally
121
+ notifier = fire_replaced_class_double("EmailNotifier")
122
+
123
+ # Alternately, you can use this fluent interface
124
+ notifier = fire_class_double("EmailNotifier").as_replaced_constant
125
+
126
+ notifier.should_receive(:notify).with("suspended as")
127
+
128
+ user = User.new
129
+ user.suspend!
130
+ end
131
+ end
132
+
133
+ This will probably become the default behaviour once we figure out a better
134
+ name for it.
135
+
136
+ ### Stubbing Constants
137
+
138
+ The constant stubbing logic used when doubling class constants can be
139
+ used for any constant.
140
+
141
+ class MapReduceRunner
142
+ ITEMS_PER_BATCH = 1000
143
+ end
144
+
145
+ describe MapReduceRunner, "when it has too many items for one batch" do
146
+ it "breaks the items up into smaller batches" do
147
+ # the test would be really slow if we had to make more than 1000 items,
148
+ # so let's change the threshold for this one test.
149
+ stub_const("MapReduceRunner::ITEMS_PER_BATCH", 10)
150
+
151
+ MapReduceRunner.run_with(twenty_items)
152
+ end
153
+ end
154
+
155
+ ### Transferring nested constants to doubled constants
156
+
157
+ When you use `fire_replaced_class_double` to replace a class or module
158
+ that also acts as a namespace for other classes and constants, your
159
+ access to these constants is cut off for the duration of the example
160
+ (since the doubled constant does not automatically have all of the
161
+ nested constants). The `:transfer_nested_constants` option is provided
162
+ to deal with this:
163
+
164
+ module MyCoolGem
165
+ class Widget
166
+ end
167
+ end
168
+
169
+ # once you do this, you can no longer access MyCoolGem::Widget in your example...
170
+ fire_replaced_class_double("MyCoolGem")
171
+
172
+ # ...unless you tell rspec-fire to transfer all nested constants
173
+ fire_class_double("MyCoolGem").as_replaced_constant(:transfer_nested_constants => true)
174
+
175
+ # ...or give it a list of constants to transfer
176
+ fire_class_double("MyCoolGem").as_replaced_constant(:transfer_nested_constants => [:Widget])
177
+
178
+ # You can also use this when using #stub_const directly
179
+ stub_const("MyCoolGem", :transfer_nested_constants => true)
180
+
100
181
  ### Doubling class methods
101
182
 
102
183
  Particularly handy for `ActiveRecord` finders. Use `fire_class_double`. If you
data/lib/rspec/fire.rb CHANGED
@@ -5,11 +5,11 @@ require 'delegate'
5
5
  module RSpec
6
6
  module Fire
7
7
  module RecursiveConstMethods
8
- def recursive_const_get object, name
8
+ def recursive_const_get name
9
9
  name.split('::').inject(Object) {|klass,name| klass.const_get name }
10
10
  end
11
11
 
12
- def recursive_const_defined? object, name
12
+ def recursive_const_defined? name
13
13
  !!name.split('::').inject(Object) {|klass,name|
14
14
  if klass && klass.const_defined?(name)
15
15
  klass.const_get name
@@ -24,10 +24,11 @@ module RSpec
24
24
 
25
25
  AM = RSpec::Mocks::ArgumentMatchers
26
26
 
27
- def initialize(doubled_class_name, method_finder, backing)
28
- @doubled_class_name = doubled_class_name
27
+ def initialize(double, method_finder, backing)
28
+ @double = double
29
29
  @method_finder = method_finder
30
30
  @backing = backing
31
+ @sym = backing.respond_to?(:sym) ? @backing.sym : @backing.message
31
32
  super(backing)
32
33
  end
33
34
 
@@ -50,10 +51,8 @@ module RSpec
50
51
  protected
51
52
 
52
53
  def ensure_arity(actual)
53
- if recursive_const_defined?(Object, @doubled_class_name)
54
- recursive_const_get(Object, @doubled_class_name).
55
- send(@method_finder, sym).
56
- should have_arity(actual)
54
+ @double.with_doubled_class do |klass|
55
+ klass.send(@method_finder, @sym).should have_arity(actual)
57
56
  end
58
57
  end
59
58
 
@@ -69,23 +68,13 @@ module RSpec
69
68
  end
70
69
  end
71
70
 
72
- class FireDouble < RSpec::Mocks::Mock
71
+ module FireDoublable
73
72
  extend RSpec::Matchers::DSL
74
73
  include RecursiveConstMethods
75
74
 
76
- def initialize(doubled_class, *args)
77
- args << {} unless Hash === args.last
78
-
79
- @__doubled_class_name = doubled_class
80
-
81
- # __declared_as copied from rspec/mocks definition of `double`
82
- args.last[:__declared_as] = 'FireDouble'
83
- super(doubled_class, *args)
84
- end
85
-
86
75
  def should_receive(method_name)
87
76
  ensure_implemented(method_name)
88
- ShouldProxy.new(@__doubled_class_name, @__method_finder, super)
77
+ ShouldProxy.new(self, @__method_finder, super)
89
78
  end
90
79
 
91
80
  def should_not_receive(method_name)
@@ -98,12 +87,17 @@ module RSpec
98
87
  super
99
88
  end
100
89
 
90
+ def with_doubled_class
91
+ if recursive_const_defined?(@__doubled_class_name)
92
+ yield recursive_const_get(@__doubled_class_name)
93
+ end
94
+ end
95
+
101
96
  protected
102
97
 
103
98
  def ensure_implemented(*method_names)
104
- if recursive_const_defined?(Object, @__doubled_class_name)
105
- recursive_const_get(Object, @__doubled_class_name).
106
- should implement(method_names, @__checked_methods)
99
+ with_doubled_class do |klass|
100
+ klass.should implement(method_names, @__checked_methods)
107
101
  end
108
102
  end
109
103
 
@@ -131,20 +125,190 @@ module RSpec
131
125
  end
132
126
  end
133
127
 
134
- class FireObjectDouble < FireDouble
135
- def initialize(*args)
128
+ class FireObjectDouble < RSpec::Mocks::Mock
129
+ include FireDoublable
130
+
131
+ def initialize(doubled_class, *args)
132
+ args << {} unless Hash === args.last
133
+
134
+ @__doubled_class_name = doubled_class
135
+
136
+ # __declared_as copied from rspec/mocks definition of `double`
137
+ args.last[:__declared_as] = 'FireDouble'
136
138
  super
137
139
  @__checked_methods = :public_instance_methods
138
140
  @__method_finder = :instance_method
139
141
  end
140
142
  end
141
143
 
142
- class FireClassDouble < FireDouble
143
- def initialize(*args)
144
- super
145
- @__checked_methods = :public_methods
146
- @__method_finder = :method
144
+ class FireClassDoubleBuilder
145
+ def self.build(doubled_class, stubs = {})
146
+ Module.new do
147
+ extend FireDoublable
148
+
149
+ @__doubled_class_name = doubled_class
150
+ @__checked_methods = :public_methods
151
+ @__method_finder = :method
152
+
153
+ stubs.each do |message, response|
154
+ stub(message).and_return(response)
155
+ end
156
+
157
+ def self.as_replaced_constant(options = {})
158
+ @__original_class = ConstantStubber.stub!(@__doubled_class_name, self, options)
159
+ extend AsReplacedConstant
160
+ self
161
+ end
162
+
163
+ def self.to_s
164
+ @__doubled_class_name + " (fire double)"
165
+ end
166
+
167
+ def self.inspect
168
+ to_s
169
+ end
170
+
171
+ def self.name
172
+ @__doubled_class_name
173
+ end
174
+
175
+ def self.method_missing(name, *args)
176
+ __mock_proxy.raise_unexpected_message_error(name, *args)
177
+ end
178
+ end
179
+ end
180
+
181
+ module AsReplacedConstant
182
+ def with_doubled_class
183
+ yield @__original_class if @__original_class
184
+ end
185
+ end
186
+ end
187
+
188
+ class ConstantStubber
189
+ extend RecursiveConstMethods
190
+
191
+ class DefinedConstantReplacer
192
+ include RecursiveConstMethods
193
+ attr_reader :original_value
194
+
195
+ def initialize(full_constant_name, stubbed_value, transfer_nested_constants)
196
+ @full_constant_name = full_constant_name
197
+ @stubbed_value = stubbed_value
198
+ @transfer_nested_constants = transfer_nested_constants
199
+ end
200
+
201
+ def stub!
202
+ context_parts = @full_constant_name.split('::')
203
+ @const_name = context_parts.pop
204
+ @context = recursive_const_get(context_parts.join('::'))
205
+ @original_value = @context.const_get(@const_name)
206
+
207
+ constants_to_transfer = verify_constants_to_transfer!
208
+
209
+ @context.send(:remove_const, @const_name)
210
+ @context.const_set(@const_name, @stubbed_value)
211
+
212
+ transfer_nested_constants(constants_to_transfer)
213
+ end
214
+
215
+ def rspec_reset
216
+ if recursive_const_get(@full_constant_name).equal?(@stubbed_value)
217
+ @context.send(:remove_const, @const_name)
218
+ @context.const_set(@const_name, @original_value)
219
+ end
220
+ end
221
+
222
+ def transfer_nested_constants(constants)
223
+ constants.each do |const|
224
+ @stubbed_value.const_set(const, original_value.const_get(const))
225
+ end
226
+ end
227
+
228
+ def verify_constants_to_transfer!
229
+ return [] unless @transfer_nested_constants
230
+
231
+ { @original_value => "the original value", @stubbed_value => "the stubbed value" }.each do |value, description|
232
+ unless value.respond_to?(:constants)
233
+ raise ArgumentError,
234
+ "Cannot transfer nested constants for #{@full_constant_name} " +
235
+ "since #{description} is not a class or module and only classes " +
236
+ "and modules support nested constants."
237
+ end
238
+ end
239
+
240
+ if @transfer_nested_constants.is_a?(Array)
241
+ @transfer_nested_constants = @transfer_nested_constants.map(&:to_s) if RUBY_VERSION == '1.8.7'
242
+ undefined_constants = @transfer_nested_constants - @original_value.constants
243
+
244
+ if undefined_constants.any?
245
+ available_constants = @original_value.constants - @transfer_nested_constants
246
+ raise ArgumentError,
247
+ "Cannot transfer nested constant(s) #{undefined_constants.join(' and ')} " +
248
+ "for #{@full_constant_name} since they are not defined. Did you mean " +
249
+ "#{available_constants.join(' or ')}?"
250
+ end
251
+
252
+ @transfer_nested_constants
253
+ else
254
+ @original_value.constants
255
+ end
256
+ end
257
+ end
258
+
259
+ class UndefinedConstantSetter
260
+ include RecursiveConstMethods
261
+
262
+ def initialize(full_constant_name, stubbed_value)
263
+ @full_constant_name = full_constant_name
264
+ @stubbed_value = stubbed_value
265
+ end
266
+
267
+ def original_value
268
+ # always nil
269
+ end
270
+
271
+ def stub!
272
+ context_parts = @full_constant_name.split('::')
273
+ const_name = context_parts.pop
274
+
275
+ remaining_parts = context_parts.dup
276
+ @deepest_defined_const = context_parts.inject(Object) do |klass, name|
277
+ break klass unless klass.const_defined?(name)
278
+ remaining_parts.shift
279
+ klass.const_get(name)
280
+ end
281
+
282
+ context = remaining_parts.inject(@deepest_defined_const) do |klass, name|
283
+ klass.const_set(name, Module.new)
284
+ end
285
+
286
+ @const_to_remove = remaining_parts.first || const_name
287
+ context.const_set(const_name, @stubbed_value)
288
+ end
289
+
290
+ def rspec_reset
291
+ if recursive_const_get(@full_constant_name).equal?(@stubbed_value)
292
+ @deepest_defined_const.send(:remove_const, @const_to_remove)
293
+ end
294
+ end
147
295
  end
296
+
297
+ def self.stub!(constant_name, value, options = {})
298
+ stubber = if recursive_const_defined?(constant_name)
299
+ DefinedConstantReplacer.new(constant_name, value, options[:transfer_nested_constants])
300
+ else
301
+ UndefinedConstantSetter.new(constant_name, value)
302
+ end
303
+
304
+ stubber.stub!
305
+ ::RSpec::Mocks.space.add(stubber)
306
+ stubber.original_value
307
+ end
308
+ end
309
+
310
+ def stub_const(name, value, options = {})
311
+ ConstantStubber.stub!(name, value, options)
148
312
  end
149
313
 
150
314
  def fire_double(*args)
@@ -152,7 +316,11 @@ module RSpec
152
316
  end
153
317
 
154
318
  def fire_class_double(*args)
155
- FireClassDouble.new(*args)
319
+ FireClassDoubleBuilder.build(*args)
320
+ end
321
+
322
+ def fire_replaced_class_double(*args)
323
+ fire_class_double(*args).as_replaced_constant
156
324
  end
157
325
  end
158
326
  end
data/rspec-fire.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'rspec-fire'
3
- s.version = '0.2.2'
3
+ s.version = '0.3.0'
4
4
  s.summary = 'More resilient test doubles for RSpec.'
5
5
  s.platform = Gem::Platform::RUBY
6
6
  s.authors = ["Xavier Shay"]
@@ -1,5 +1,15 @@
1
1
  require 'spec_helper'
2
2
 
3
+ TOP_LEVEL_VALUE_CONST = 7
4
+
5
+ RSpec::Mocks::Space.class_eval do
6
+ # Deal with the fact that #mocks was renamed to #receivers for RSpec 2.9:
7
+ # https://github.com/rspec/rspec-mocks/commit/17c259ea5143d309e90ca6d53d40f6356ac2d0a5
8
+ unless private_instance_methods.map(&:to_sym).include?(:receivers)
9
+ alias_method :receivers, :mocks
10
+ end
11
+ end
12
+
3
13
  module TestMethods
4
14
  def defined_method
5
15
  raise "Y U NO MOCK?"
@@ -16,6 +26,14 @@ end
16
26
 
17
27
  class TestClass
18
28
  extend TestMethods
29
+
30
+ M = :m
31
+ N = :n
32
+
33
+ class Nested
34
+ class NestedEvenMore
35
+ end
36
+ end
19
37
  end
20
38
 
21
39
  shared_examples_for 'a fire-enhanced double method' do
@@ -134,4 +152,294 @@ describe '#fire_class_double' do
134
152
  let(:doubled_object) { fire_class_double("TestClass") }
135
153
 
136
154
  it_should_behave_like 'a fire-enhanced double'
155
+
156
+ it 'uses a module for the doubled object so that it supports nested constants like a real class' do
157
+ doubled_object.should be_a(Module)
158
+ end
159
+
160
+ it 'has a readable string representation' do
161
+ doubled_object.to_s.should include("TestClass (fire double)")
162
+ doubled_object.inspect.should include("TestClass (fire double)")
163
+ end
164
+
165
+ it 'assigns the class name' do
166
+ TestClass.name.should eq("TestClass")
167
+ doubled_object.name.should eq("TestClass")
168
+ end
169
+
170
+ it 'raises a mock expectation error for undefind methods' do
171
+ expect {
172
+ doubled_object.abc
173
+ }.to raise_error(RSpec::Mocks::MockExpectationError)
174
+ end
175
+
176
+ it 'allows stubs to be specified as a hash' do
177
+ double = fire_class_double("SomeClass", :a => 5, :b => 8)
178
+ double.a.should eq(5)
179
+ double.b.should eq(8)
180
+ end
181
+ end
182
+
183
+ def reset_rspec_mocks
184
+ ::RSpec::Mocks.space.reset_all
185
+ end
186
+
187
+ describe '#fire_replaced_class_double (for an existing class)' do
188
+ let(:doubled_object) { fire_replaced_class_double("TestClass") }
189
+
190
+ it_should_behave_like 'a fire-enhanced double'
191
+
192
+ it 'replaces the constant for the duration of the test' do
193
+ orig_class = TestClass
194
+ doubled_object.should_not be(orig_class)
195
+ TestClass.should be(doubled_object)
196
+ reset_rspec_mocks
197
+ TestClass.should be(orig_class)
198
+ end
199
+
200
+ it 'supports transferring nested constants to the double' do
201
+ fire_class_double("TestClass").as_replaced_constant(:transfer_nested_constants => true)
202
+ TestClass::M.should eq(:m)
203
+ TestClass::N.should eq(:n)
204
+ end
205
+ end
206
+
207
+ describe '#fire_replaced_class_double (for a non-existant class)' do
208
+ it 'allows any method to be mocked' do
209
+ double = fire_replaced_class_double("A::B::C")
210
+ double.should_receive(:foo).with("a").and_return(:bar)
211
+ A::B::C.foo("a").should eq(:bar)
212
+ end
213
+ end
214
+
215
+ shared_examples_for "loaded constant stubbing" do |const_name|
216
+ include RSpec::Fire::RecursiveConstMethods
217
+ let!(:original_const_value) { const }
218
+ after { change_const_value_to(original_const_value) }
219
+
220
+ define_method :const do
221
+ recursive_const_get(const_name)
222
+ end
223
+
224
+ define_method :parent_const do
225
+ recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
226
+ end
227
+
228
+ define_method :last_const_part do
229
+ const_name.split('::').last
230
+ end
231
+
232
+ def change_const_value_to(value)
233
+ parent_const.send(:remove_const, last_const_part)
234
+ parent_const.const_set(last_const_part, value)
235
+ end
236
+
237
+ it 'allows it to be stubbed' do
238
+ const.should_not eq(7)
239
+ stub_const(const_name, 7)
240
+ const.should eq(7)
241
+ end
242
+
243
+ it 'resets it to its original value when rspec clears its mocks' do
244
+ original_value = const
245
+ original_value.should_not eq(:a)
246
+ stub_const(const_name, :a)
247
+ reset_rspec_mocks
248
+ const.should be(original_value)
249
+ end
250
+
251
+ it 'does not reset the value to its original value when rspec clears its mocks if the example modifies the value of the constant' do
252
+ stub_const(const_name, :a)
253
+ change_const_value_to(new_const_value = Object.new)
254
+ reset_rspec_mocks
255
+ const.should be(new_const_value)
256
+ end
257
+
258
+ it 'returns the original value' do
259
+ orig_value = const
260
+ returned_value = stub_const(const_name, 7)
261
+ returned_value.should be(orig_value)
262
+ end
263
+ end
264
+
265
+ shared_examples_for "unloaded constant stubbing" do |const_name|
266
+ include RSpec::Fire::RecursiveConstMethods
267
+ before { recursive_const_defined?(const_name).should be_false }
268
+
269
+ define_method :const do
270
+ recursive_const_get(const_name)
271
+ end
272
+
273
+ define_method :parent_const do
274
+ recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
275
+ end
276
+
277
+ define_method :last_const_part do
278
+ const_name.split('::').last
279
+ end
280
+
281
+ def change_const_value_to(value)
282
+ parent_const.send(:remove_const, last_const_part)
283
+ parent_const.const_set(last_const_part, value)
284
+ end
285
+
286
+ it 'allows it to be stubbed' do
287
+ stub_const(const_name, 7)
288
+ const.should eq(7)
289
+ end
290
+
291
+ it 'removes the constant when rspec clears its mocks' do
292
+ stub_const(const_name, 7)
293
+ reset_rspec_mocks
294
+ recursive_const_defined?(const_name).should be_false
295
+ end
296
+
297
+ it 'does not remove the constant when the example manually sets it' do
298
+ begin
299
+ stub_const(const_name, 7)
300
+ stubber = RSpec::Mocks.space.send(:receivers).first
301
+ change_const_value_to(new_const_value = Object.new)
302
+ reset_rspec_mocks
303
+ const.should equal(new_const_value)
304
+ ensure
305
+ change_const_value_to(7)
306
+ stubber.rspec_reset
307
+ end
308
+ end
309
+
310
+ it 'returns nil since it was not originally set' do
311
+ stub_const(const_name, 7).should be_nil
312
+ end
313
+
314
+ it 'ignores the :transfer_nested_constants if passed' do
315
+ stub = Module.new
316
+ stub_const(const_name, stub, :transfer_nested_constants => true)
317
+ stub.constants.should eq([])
318
+ end
319
+ end
320
+
321
+ describe "#stub_const" do
322
+ context 'for a loaded unnested constant' do
323
+ it_behaves_like "loaded constant stubbing", "TestClass"
324
+
325
+ it 'allows nested constants to be transferred to a stub module' do
326
+ tc_nested = TestClass::Nested
327
+ stub = Module.new
328
+ stub_const("TestClass", stub, :transfer_nested_constants => true)
329
+ stub::M.should eq(:m)
330
+ stub::N.should eq(:n)
331
+ stub::Nested.should be(tc_nested)
332
+ end
333
+
334
+ it 'allows nested constants to be selectively transferred to a stub module' do
335
+ stub = Module.new
336
+ stub_const("TestClass", stub, :transfer_nested_constants => [:M, :N])
337
+ stub::M.should eq(:m)
338
+ stub::N.should eq(:n)
339
+ defined?(stub::Nested).should be_false
340
+ end
341
+
342
+ it 'raises an error if asked to transfer nested constants but given an object that does not support them' do
343
+ original_tc = TestClass
344
+ stub = Object.new
345
+ expect {
346
+ stub_const("TestClass", stub, :transfer_nested_constants => true)
347
+ }.to raise_error(ArgumentError)
348
+
349
+ TestClass.should be(original_tc)
350
+
351
+ expect {
352
+ stub_const("TestClass", stub, :transfer_nested_constants => [:M])
353
+ }.to raise_error(ArgumentError)
354
+
355
+ TestClass.should be(original_tc)
356
+ end
357
+
358
+ it 'raises an error if asked to transfer nested constants on a constant that does not support nested constants' do
359
+ stub = Module.new
360
+ expect {
361
+ stub_const("TOP_LEVEL_VALUE_CONST", stub, :transfer_nested_constants => true)
362
+ }.to raise_error(ArgumentError)
363
+
364
+ TOP_LEVEL_VALUE_CONST.should eq(7)
365
+
366
+ expect {
367
+ stub_const("TOP_LEVEL_VALUE_CONST", stub, :transfer_nested_constants => [:M])
368
+ }.to raise_error(ArgumentError)
369
+
370
+ TOP_LEVEL_VALUE_CONST.should eq(7)
371
+ end
372
+
373
+ it 'raises an error if asked to transfer a nested constant that is not defined' do
374
+ original_tc = TestClass
375
+ defined?(TestClass::V).should be_false
376
+ stub = Module.new
377
+
378
+ expect {
379
+ stub_const("TestClass", stub, :transfer_nested_constants => [:V])
380
+ }.to raise_error(/cannot transfer nested constant.*V/i)
381
+
382
+ TestClass.should be(original_tc)
383
+ end
384
+ end
385
+
386
+ context 'for a loaded nested constant' do
387
+ it_behaves_like "loaded constant stubbing", "TestClass::Nested"
388
+ end
389
+
390
+ context 'for a loaded deeply nested constant' do
391
+ it_behaves_like "loaded constant stubbing", "TestClass::Nested::NestedEvenMore"
392
+ end
393
+
394
+ context 'for an unloaded unnested constant' do
395
+ it_behaves_like "unloaded constant stubbing", "X"
396
+ end
397
+
398
+ context 'for an unloaded nested constant' do
399
+ it_behaves_like "unloaded constant stubbing", "X::Y"
400
+
401
+ it 'removes the root constant when rspec clears its mocks' do
402
+ defined?(X).should be_false
403
+ stub_const("X::Y", 7)
404
+ reset_rspec_mocks
405
+ defined?(X).should be_false
406
+ end
407
+ end
408
+
409
+ context 'for an unloaded deeply nested constant' do
410
+ it_behaves_like "unloaded constant stubbing", "X::Y::Z"
411
+
412
+ it 'removes the root constant when rspec clears its mocks' do
413
+ defined?(X).should be_false
414
+ stub_const("X::Y::Z", 7)
415
+ reset_rspec_mocks
416
+ defined?(X).should be_false
417
+ end
418
+ end
419
+
420
+ context 'for an unloaded constant nested within a loaded constant' do
421
+ it_behaves_like "unloaded constant stubbing", "TestClass::X"
422
+
423
+ it 'removes the unloaded constant but leaves the loaded constant when rspec resets its mocks' do
424
+ defined?(TestClass).should be_true
425
+ defined?(TestClass::X).should be_false
426
+ stub_const("TestClass::X", 7)
427
+ reset_rspec_mocks
428
+ defined?(TestClass).should be_true
429
+ defined?(TestClass::X).should be_false
430
+ end
431
+ end
432
+
433
+ context 'for an unloaded constant nested deeply within a deeply nested loaded constant' do
434
+ it_behaves_like "unloaded constant stubbing", "TestClass::Nested::NestedEvenMore::X::Y::Z"
435
+
436
+ it 'removes the first unloaded constant but leaves the loaded nested constant when rspec resets its mocks' do
437
+ defined?(TestClass::Nested::NestedEvenMore).should be_true
438
+ defined?(TestClass::Nested::NestedEvenMore::X).should be_false
439
+ stub_const("TestClass::Nested::NestedEvenMore::X::Y::Z", 7)
440
+ reset_rspec_mocks
441
+ defined?(TestClass::Nested::NestedEvenMore).should be_true
442
+ defined?(TestClass::Nested::NestedEvenMore::X).should be_false
443
+ end
444
+ end
137
445
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-fire
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-31 00:00:00.000000000 Z
12
+ date: 2012-03-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &2156305180 !ruby/object:Gem::Requirement
16
+ requirement: &2153330640 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2156305180
24
+ version_requirements: *2153330640
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &2156304600 !ruby/object:Gem::Requirement
27
+ requirement: &2153330120 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '2.5'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2156304600
35
+ version_requirements: *2153330120
36
36
  description:
37
37
  email:
38
38
  - hello@xaviershay.com