rspec-fire 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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