rspec-mocks 2.11.3 → 2.12.0

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