rspec-mocks 2.11.3 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. data/Changelog.md +16 -0
  2. data/README.md +26 -1
  3. data/features/argument_matchers/explicit.feature +2 -2
  4. data/features/argument_matchers/general_matchers.feature +4 -4
  5. data/features/argument_matchers/type_matchers.feature +1 -1
  6. data/features/message_expectations/README.md +4 -0
  7. data/features/message_expectations/any_instance.feature +1 -1
  8. data/features/message_expectations/call_original.feature +24 -0
  9. data/features/message_expectations/expect_message.feature +5 -5
  10. data/features/message_expectations/receive_counts.feature +7 -7
  11. data/features/message_expectations/warn_when_expectation_is_set_on_nil.feature +3 -3
  12. data/features/method_stubs/any_instance.feature +6 -7
  13. data/features/method_stubs/as_null_object.feature +1 -1
  14. data/features/method_stubs/simple_return_value.feature +2 -2
  15. data/features/method_stubs/stub_chain.feature +1 -1
  16. data/features/method_stubs/stub_implementation.feature +1 -1
  17. data/features/method_stubs/to_ary.feature +1 -1
  18. data/features/{stubbing_constants → mutating_constants}/README.md +21 -1
  19. data/features/mutating_constants/hiding_defined_constant.feature +64 -0
  20. data/features/{stubbing_constants → mutating_constants}/stub_defined_constant.feature +0 -0
  21. data/features/{stubbing_constants → mutating_constants}/stub_undefined_constant.feature +0 -0
  22. data/features/outside_rspec/configuration.feature +3 -3
  23. data/features/outside_rspec/standalone.feature +6 -5
  24. data/lib/rspec/mocks.rb +17 -1
  25. data/lib/rspec/mocks/any_instance.rb +12 -12
  26. data/lib/rspec/mocks/configuration.rb +28 -0
  27. data/lib/rspec/mocks/error_generator.rb +6 -0
  28. data/lib/rspec/mocks/example_methods.rb +34 -9
  29. data/lib/rspec/mocks/framework.rb +3 -2
  30. data/lib/rspec/mocks/instance_method_stasher.rb +70 -0
  31. data/lib/rspec/mocks/message_expectation.rb +49 -29
  32. data/lib/rspec/mocks/method_double.rb +84 -7
  33. data/lib/rspec/mocks/{stub_const.rb → mutate_const.rb} +117 -40
  34. data/lib/rspec/mocks/proxy.rb +16 -5
  35. data/lib/rspec/mocks/version.rb +1 -1
  36. data/spec/rspec/mocks/and_call_original_spec.rb +162 -0
  37. data/spec/rspec/mocks/any_instance_spec.rb +18 -7
  38. data/spec/rspec/mocks/configuration_spec.rb +26 -0
  39. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +9 -10
  40. data/spec/rspec/mocks/instance_method_stasher_spec.rb +58 -0
  41. data/spec/rspec/mocks/mock_spec.rb +35 -35
  42. data/spec/rspec/mocks/{stub_const_spec.rb → mutate_const_spec.rb} +142 -13
  43. data/spec/rspec/mocks/null_object_mock_spec.rb +3 -2
  44. data/spec/rspec/mocks/partial_mock_spec.rb +102 -77
  45. data/spec/rspec/mocks/serialization_spec.rb +1 -2
  46. data/spec/rspec/mocks/stub_implementation_spec.rb +6 -6
  47. data/spec/rspec/mocks_spec.rb +7 -0
  48. data/spec/spec_helper.rb +11 -0
  49. metadata +79 -80
  50. data/lib/rspec/mocks/stashed_instance_method.rb +0 -60
  51. data/spec/rspec/mocks/stashed_instance_method_spec.rb +0 -53
@@ -18,17 +18,14 @@ end
18
18
 
19
19
  module RSpec
20
20
  module Mocks
21
- describe "Constant Stubbing" do
21
+ describe "Constant Mutating" do
22
22
  include RSpec::Mocks::RecursiveConstMethods
23
23
 
24
24
  def reset_rspec_mocks
25
25
  ::RSpec::Mocks.space.reset_all
26
26
  end
27
27
 
28
- shared_examples_for "loaded constant stubbing" do |const_name|
29
- let!(:original_const_value) { const }
30
- after { change_const_value_to(original_const_value) }
31
-
28
+ shared_context "constant example methods" do |const_name|
32
29
  define_method :const do
33
30
  recursive_const_get(const_name)
34
31
  end
@@ -40,6 +37,13 @@ module RSpec
40
37
  define_method :last_const_part do
41
38
  const_name.split('::').last
42
39
  end
40
+ end
41
+
42
+ shared_examples_for "loaded constant stubbing" do |const_name|
43
+ include_context "constant example methods", const_name
44
+
45
+ let!(:original_const_value) { const }
46
+ after { change_const_value_to(original_const_value) }
43
47
 
44
48
  def change_const_value_to(value)
45
49
  parent_const.send(:remove_const, last_const_part)
@@ -65,20 +69,29 @@ module RSpec
65
69
  end
66
70
  end
67
71
 
68
- shared_examples_for "unloaded constant stubbing" do |const_name|
69
- before { recursive_const_defined?(const_name).should be_false }
72
+ shared_examples_for "loaded constant hiding" do |const_name|
73
+ before { recursive_const_defined?(const_name).should be_true }
70
74
 
71
- define_method :const do
72
- recursive_const_get(const_name)
75
+ it 'allows it to be hidden' do
76
+ hide_const(const_name)
77
+ recursive_const_defined?(const_name).should be_false
73
78
  end
74
79
 
75
- define_method :parent_const do
76
- recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, ''))
80
+ it 'resets the constant when rspec clear its mocks' do
81
+ hide_const(const_name)
82
+ reset_rspec_mocks
83
+ recursive_const_defined?(const_name).should be_true
77
84
  end
78
85
 
79
- define_method :last_const_part do
80
- const_name.split('::').last
86
+ it 'returns nil' do
87
+ hide_const(const_name).should be_nil
81
88
  end
89
+ end
90
+
91
+ shared_examples_for "unloaded constant stubbing" do |const_name|
92
+ include_context "constant example methods", const_name
93
+
94
+ before { recursive_const_defined?(const_name).should be_false }
82
95
 
83
96
  it 'allows it to be stubbed' do
84
97
  stub_const(const_name, 7)
@@ -102,6 +115,85 @@ module RSpec
102
115
  end
103
116
  end
104
117
 
118
+ shared_examples_for "unloaded constant hiding" do |const_name|
119
+ include_context "constant example methods", const_name
120
+
121
+ before { recursive_const_defined?(const_name).should be_false }
122
+
123
+ it 'allows it to be hidden, though the operation has no effect' do
124
+ hide_const(const_name)
125
+ recursive_const_defined?(const_name).should be_false
126
+ end
127
+
128
+ it 'remains undefined after rspec clears its mocks' do
129
+ hide_const(const_name)
130
+ reset_rspec_mocks
131
+ recursive_const_defined?(const_name).should be_false
132
+ end
133
+
134
+ it 'returns nil' do
135
+ hide_const(const_name).should be_nil
136
+ end
137
+ end
138
+
139
+ describe "#hide_const" do
140
+ context 'for a loaded nested constant' do
141
+ it_behaves_like "loaded constant hiding", "TestClass::Nested"
142
+ end
143
+
144
+ context 'for an unloaded constant with nested name that matches a top-level constant' do
145
+ it_behaves_like "unloaded constant hiding", "TestClass::Hash"
146
+
147
+ it 'does not hide the top-level constant' do
148
+ top_level_hash = ::Hash
149
+
150
+ hide_const("TestClass::Hash")
151
+ expect(::Hash).to equal(top_level_hash)
152
+ end
153
+
154
+ it 'does not affect the ability to access the top-level constant from nested contexts', :silence_warnings do
155
+ top_level_hash = ::Hash
156
+
157
+ hide_const("TestClass::Hash")
158
+ expect(TestClass::Hash).to equal(top_level_hash)
159
+ end
160
+ end
161
+
162
+ context 'for a loaded deeply nested constant' do
163
+ it_behaves_like "loaded constant hiding", "TestClass::Nested::NestedEvenMore"
164
+ end
165
+
166
+ context 'for an unloaded unnested constant' do
167
+ it_behaves_like "unloaded constant hiding", "X"
168
+ end
169
+
170
+ context 'for an unloaded nested constant' do
171
+ it_behaves_like "unloaded constant hiding", "X::Y"
172
+ end
173
+
174
+ it 'can be hidden multiple times but still restores the original value properly' do
175
+ orig_value = TestClass
176
+ hide_const("TestClass")
177
+ hide_const("TestClass")
178
+
179
+ reset_rspec_mocks
180
+ TestClass.should be(orig_value)
181
+ end
182
+
183
+ it 'allows a constant to be hidden, then stubbed, restoring it to its original value properly' do
184
+ orig_value = TOP_LEVEL_VALUE_CONST
185
+
186
+ hide_const("TOP_LEVEL_VALUE_CONST")
187
+ recursive_const_defined?("TOP_LEVEL_VALUE_CONST").should be_false
188
+
189
+ stub_const("TOP_LEVEL_VALUE_CONST", 12345)
190
+ TOP_LEVEL_VALUE_CONST.should == 12345
191
+
192
+ reset_rspec_mocks
193
+ TOP_LEVEL_VALUE_CONST.should == orig_value
194
+ end
195
+ end
196
+
105
197
  describe "#stub_const" do
106
198
  context 'for a loaded unnested constant' do
107
199
  it_behaves_like "loaded constant stubbing", "TestClass"
@@ -273,7 +365,9 @@ module RSpec
273
365
 
274
366
  it("exposes its name") { const.name.should eq("TestClass::M") }
275
367
  it("indicates it was previously defined") { const.should be_previously_defined }
368
+ it("indicates it has not been mutated") { const.should_not be_mutated }
276
369
  it("indicates it has not been stubbed") { const.should_not be_stubbed }
370
+ it("indicates it has not been hidden") { const.should_not be_hidden }
277
371
  it("exposes its original value") { const.original_value.should eq(:m) }
278
372
  end
279
373
 
@@ -283,7 +377,9 @@ module RSpec
283
377
 
284
378
  it("exposes its name") { const.name.should eq("TestClass::M") }
285
379
  it("indicates it was previously defined") { const.should be_previously_defined }
380
+ it("indicates it has been mutated") { const.should be_mutated }
286
381
  it("indicates it has been stubbed") { const.should be_stubbed }
382
+ it("indicates it has not been hidden") { const.should_not be_hidden }
287
383
  it("exposes its original value") { const.original_value.should eq(:m) }
288
384
  end
289
385
 
@@ -293,7 +389,9 @@ module RSpec
293
389
 
294
390
  it("exposes its name") { const.name.should eq("TestClass::Undefined") }
295
391
  it("indicates it was not previously defined") { const.should_not be_previously_defined }
392
+ it("indicates it has been mutated") { const.should be_mutated }
296
393
  it("indicates it has been stubbed") { const.should be_stubbed }
394
+ it("indicates it has not been hidden") { const.should_not be_hidden }
297
395
  it("returns nil for the original value") { const.original_value.should be_nil }
298
396
  end
299
397
 
@@ -302,7 +400,9 @@ module RSpec
302
400
 
303
401
  it("exposes its name") { const.name.should eq("TestClass::Undefined") }
304
402
  it("indicates it was not previously defined") { const.should_not be_previously_defined }
403
+ it("indicates it has not been mutated") { const.should_not be_mutated }
305
404
  it("indicates it has not been stubbed") { const.should_not be_stubbed }
405
+ it("indicates it has not been hidden") { const.should_not be_hidden }
306
406
  it("returns nil for the original value") { const.original_value.should be_nil }
307
407
  end
308
408
 
@@ -313,7 +413,9 @@ module RSpec
313
413
 
314
414
  it("exposes its name") { const.name.should eq("TestClass::M") }
315
415
  it("indicates it was previously defined") { const.should be_previously_defined }
416
+ it("indicates it has been mutated") { const.should be_mutated }
316
417
  it("indicates it has been stubbed") { const.should be_stubbed }
418
+ it("indicates it has not been hidden") { const.should_not be_hidden }
317
419
  it("exposes its original value") { const.original_value.should eq(:m) }
318
420
  end
319
421
 
@@ -324,9 +426,36 @@ module RSpec
324
426
 
325
427
  it("exposes its name") { const.name.should eq("TestClass::Undefined") }
326
428
  it("indicates it was not previously defined") { const.should_not be_previously_defined }
429
+ it("indicates it has been mutated") { const.should be_mutated }
327
430
  it("indicates it has been stubbed") { const.should be_stubbed }
431
+ it("indicates it has not been hidden") { const.should_not be_hidden }
328
432
  it("returns nil for the original value") { const.original_value.should be_nil }
329
433
  end
434
+
435
+ context 'for a previously defined hidden constant' do
436
+ before { hide_const("TestClass::M") }
437
+ let(:const) { Constant.original("TestClass::M") }
438
+
439
+ it("exposes its name") { const.name.should eq("TestClass::M") }
440
+ it("indicates it was previously defined") { const.should be_previously_defined }
441
+ it("indicates it has been mutated") { const.should be_mutated }
442
+ it("indicates it has not been stubbed") { const.should_not be_stubbed }
443
+ it("indicates it has been hidden") { const.should be_hidden }
444
+ it("exposes its original value") { const.original_value.should eq(:m) }
445
+ end
446
+
447
+ context 'for a previously defined constant that has been hidden twice' do
448
+ before { hide_const("TestClass::M") }
449
+ before { hide_const("TestClass::M") }
450
+ let(:const) { Constant.original("TestClass::M") }
451
+
452
+ it("exposes its name") { const.name.should eq("TestClass::M") }
453
+ it("indicates it was previously defined") { const.should be_previously_defined }
454
+ it("indicates it has been mutated") { const.should be_mutated }
455
+ it("indicates it has not been stubbed") { const.should_not be_stubbed }
456
+ it("indicates it has been hidden") { const.should be_hidden }
457
+ it("exposes its original value") { const.original_value.should eq(:m) }
458
+ end
330
459
  end
331
460
  end
332
461
  end
@@ -17,8 +17,9 @@ module RSpec
17
17
  end
18
18
 
19
19
  it "raises an error when interpolated in a string as an integer" do
20
- # Not sure why, but 1.9.2 raises a different error than 1.8.7 and 1.9.3...
21
- expected_error = RUBY_VERSION == '1.9.2' ?
20
+ # Not sure why, but 1.9.2 (but not JRuby --1.9) raises a different
21
+ # error than 1.8.7 and 1.9.3...
22
+ expected_error = (RUBY_VERSION == '1.9.2' && RUBY_PLATFORM !~ /java/) ?
22
23
  RSpec::Mocks::MockExpectationError :
23
24
  TypeError
24
25
 
@@ -3,93 +3,91 @@ require 'spec_helper'
3
3
  module RSpec
4
4
  module Mocks
5
5
  describe "using a Partial Mock," do
6
- before(:each) do
7
- @object = Object.new
8
- end
9
-
6
+ let(:object) { Object.new }
7
+
10
8
  it "names the class in the failure message" do
11
- @object.should_receive(:foo)
9
+ object.should_receive(:foo)
12
10
  expect do
13
- @object.rspec_verify
11
+ object.rspec_verify
14
12
  end.to raise_error(RSpec::Mocks::MockExpectationError, /\(#<Object:.*>\).foo/)
15
13
  end
16
-
14
+
17
15
  it "names the class in the failure message when expectation is on class" do
18
16
  Object.should_receive(:foo)
19
17
  lambda do
20
18
  Object.rspec_verify
21
19
  end.should raise_error(RSpec::Mocks::MockExpectationError, /<Object \(class\)>/)
22
20
  end
23
-
21
+
24
22
  it "does not conflict with @options in the object" do
25
- @object.instance_eval { @options = Object.new }
26
- @object.should_receive(:blah)
27
- @object.blah
23
+ object.instance_eval { @options = Object.new }
24
+ object.should_receive(:blah)
25
+ object.blah
28
26
  end
29
-
27
+
30
28
  it "should_not_receive mocks out the method" do
31
- @object.should_not_receive(:fuhbar)
29
+ object.should_not_receive(:fuhbar)
32
30
  expect {
33
- @object.fuhbar
31
+ object.fuhbar
34
32
  }.to raise_error(
35
- RSpec::Mocks::MockExpectationError,
33
+ RSpec::Mocks::MockExpectationError,
36
34
  /expected\: 0 times\n received\: 1 time/
37
35
  )
38
36
  end
39
-
37
+
40
38
  it "should_not_receive returns a negative message expectation" do
41
- @object.should_not_receive(:foobar).should be_kind_of(RSpec::Mocks::NegativeMessageExpectation)
39
+ object.should_not_receive(:foobar).should be_kind_of(RSpec::Mocks::NegativeMessageExpectation)
42
40
  end
43
-
41
+
44
42
  it "should_receive mocks out the method" do
45
- @object.should_receive(:foobar).with(:test_param).and_return(1)
46
- @object.foobar(:test_param).should equal(1)
43
+ object.should_receive(:foobar).with(:test_param).and_return(1)
44
+ object.foobar(:test_param).should equal(1)
47
45
  end
48
-
46
+
49
47
  it "should_receive handles a hash" do
50
- @object.should_receive(:foobar).with(:key => "value").and_return(1)
51
- @object.foobar(:key => "value").should equal(1)
48
+ object.should_receive(:foobar).with(:key => "value").and_return(1)
49
+ object.foobar(:key => "value").should equal(1)
52
50
  end
53
-
51
+
54
52
  it "should_receive handles an inner hash" do
55
53
  hash = {:a => {:key => "value"}}
56
- @object.should_receive(:foobar).with(:key => "value").and_return(1)
57
- @object.foobar(hash[:a]).should equal(1)
54
+ object.should_receive(:foobar).with(:key => "value").and_return(1)
55
+ object.foobar(hash[:a]).should equal(1)
58
56
  end
59
-
57
+
60
58
  it "should_receive returns a message expectation" do
61
- @object.should_receive(:foobar).should be_kind_of(RSpec::Mocks::MessageExpectation)
62
- @object.foobar
59
+ object.should_receive(:foobar).should be_kind_of(RSpec::Mocks::MessageExpectation)
60
+ object.foobar
63
61
  end
64
-
62
+
65
63
  it "should_receive verifies method was called" do
66
- @object.should_receive(:foobar).with(:test_param).and_return(1)
64
+ object.should_receive(:foobar).with(:test_param).and_return(1)
67
65
  lambda do
68
- @object.rspec_verify
66
+ object.rspec_verify
69
67
  end.should raise_error(RSpec::Mocks::MockExpectationError)
70
68
  end
71
-
69
+
72
70
  it "should_receive also takes a String argument" do
73
- @object.should_receive('foobar')
74
- @object.foobar
71
+ object.should_receive('foobar')
72
+ object.foobar
75
73
  end
76
-
74
+
77
75
  it "should_not_receive also takes a String argument" do
78
- @object.should_not_receive('foobar')
76
+ object.should_not_receive('foobar')
79
77
  lambda do
80
- @object.foobar
78
+ object.foobar
81
79
  end.should raise_error(RSpec::Mocks::MockExpectationError)
82
80
  end
83
-
81
+
84
82
  it "uses reports nil in the error message" do
85
83
  allow_message_expectations_on_nil
86
-
87
- @nil = nil
88
- @nil.should_receive(:foobar)
84
+
85
+ _nil = nil
86
+ _nil.should_receive(:foobar)
89
87
  expect {
90
- @nil.rspec_verify
88
+ _nil.rspec_verify
91
89
  }.to raise_error(
92
- RSpec::Mocks::MockExpectationError,
90
+ RSpec::Mocks::MockExpectationError,
93
91
  %Q|(nil).foobar(any args)\n expected: 1 time\n received: 0 times|
94
92
  )
95
93
  end
@@ -109,61 +107,88 @@ module RSpec
109
107
  }.to raise_error(RSpec::Mocks::MockExpectationError, /MyClass/)
110
108
  end
111
109
  end
112
-
110
+
111
+ describe "Using a partial mock on a proxy object", :if => defined?(::BasicObject) do
112
+ let(:proxy_class) do
113
+ Class.new(::BasicObject) do
114
+ def initialize(target)
115
+ @target = target
116
+ end
117
+
118
+ def proxied?
119
+ true
120
+ end
121
+
122
+ def method_missing(*a)
123
+ @target.send(*a)
124
+ end
125
+ end
126
+ end
127
+
128
+ let(:instance) { proxy_class.new(Object.new) }
129
+
130
+ it 'works properly' do
131
+ instance.should_receive(:proxied?).and_return(false)
132
+ instance.should_not be_proxied
133
+ end
134
+ end
135
+
113
136
  describe "Partially mocking an object that defines ==, after another mock has been defined" do
114
137
  before(:each) do
115
138
  stub("existing mock", :foo => :foo)
116
139
  end
117
-
118
- class PartiallyMockedEquals
119
- attr_reader :val
120
- def initialize(val)
121
- @val = val
122
- end
123
-
124
- def ==(other)
125
- @val == other.val
140
+
141
+ let(:klass) do
142
+ Class.new do
143
+ attr_reader :val
144
+ def initialize(val)
145
+ @val = val
146
+ end
147
+
148
+ def ==(other)
149
+ @val == other.val
150
+ end
126
151
  end
127
152
  end
128
-
153
+
129
154
  it "does not raise an error when stubbing the object" do
130
- o = PartiallyMockedEquals.new :foo
131
- lambda { o.stub(:bar) }.should_not raise_error(NoMethodError)
155
+ o = klass.new :foo
156
+ expect { o.stub(:bar) }.not_to raise_error(NoMethodError)
132
157
  end
133
158
  end
134
159
 
135
160
  describe "Method visibility when using partial mocks" do
136
- class MockableClass
137
- def public_method
138
- private_method
139
- protected_method
161
+ let(:klass) do
162
+ Class.new do
163
+ def public_method
164
+ private_method
165
+ protected_method
166
+ end
167
+ protected
168
+ def protected_method; end
169
+ private
170
+ def private_method; end
140
171
  end
141
- protected
142
- def protected_method; end
143
- private
144
- def private_method; end
145
172
  end
146
173
 
147
- before(:each) do
148
- @object = MockableClass.new
149
- end
174
+ let(:object) { klass.new }
150
175
 
151
176
  it 'keeps public methods public' do
152
- @object.should_receive(:public_method)
153
- @object.public_methods.should include_method(:public_method)
154
- @object.public_method
177
+ object.should_receive(:public_method)
178
+ object.public_methods.should include_method(:public_method)
179
+ object.public_method
155
180
  end
156
181
 
157
182
  it 'keeps private methods private' do
158
- @object.should_receive(:private_method)
159
- @object.private_methods.should include_method(:private_method)
160
- @object.public_method
183
+ object.should_receive(:private_method)
184
+ object.private_methods.should include_method(:private_method)
185
+ object.public_method
161
186
  end
162
187
 
163
188
  it 'keeps protected methods protected' do
164
- @object.should_receive(:protected_method)
165
- @object.protected_methods.should include_method(:protected_method)
166
- @object.public_method
189
+ object.should_receive(:protected_method)
190
+ object.protected_methods.should include_method(:protected_method)
191
+ object.public_method
167
192
  end
168
193
 
169
194
  end