rspec-mocks 3.0.0.beta1 → 3.0.0.beta2

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 (114) hide show
  1. data.tar.gz.sig +1 -1
  2. data/Changelog.md +95 -3
  3. data/README.md +27 -13
  4. data/features/README.md +15 -7
  5. data/features/argument_matchers/README.md +5 -5
  6. data/features/argument_matchers/explicit.feature +6 -6
  7. data/features/argument_matchers/general_matchers.feature +4 -4
  8. data/features/argument_matchers/type_matchers.feature +2 -2
  9. data/features/message_expectations/README.md +29 -27
  10. data/features/message_expectations/call_original.feature +0 -1
  11. data/features/message_expectations/message_chains_using_expect.feature +49 -0
  12. data/features/method_stubs/README.md +2 -1
  13. data/features/method_stubs/{any_instance.feature → allow_any_instance_of.feature} +12 -12
  14. data/features/method_stubs/{stub_chain.feature → receive_message_chain.feature} +3 -3
  15. data/features/method_stubs/to_ary.feature +1 -1
  16. data/features/mutating_constants/stub_defined_constant.feature +0 -1
  17. data/features/outside_rspec/standalone.feature +1 -1
  18. data/features/spies/spy_pure_mock_method.feature +1 -1
  19. data/features/test_frameworks/test_unit.feature +21 -10
  20. data/features/verifying_doubles/README.md +17 -0
  21. data/features/verifying_doubles/class_doubles.feature +1 -16
  22. data/features/verifying_doubles/dynamic_classes.feature +0 -1
  23. data/features/verifying_doubles/{introduction.feature → instance_doubles.feature} +41 -23
  24. data/features/verifying_doubles/partial_doubles.feature +2 -2
  25. data/lib/rspec/mocks.rb +69 -82
  26. data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +35 -0
  27. data/lib/rspec/mocks/any_instance/expectation_chain.rb +1 -2
  28. data/lib/rspec/mocks/any_instance/recorder.rb +52 -18
  29. data/lib/rspec/mocks/any_instance/stub_chain.rb +1 -1
  30. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +4 -0
  31. data/lib/rspec/mocks/argument_list_matcher.rb +10 -44
  32. data/lib/rspec/mocks/argument_matchers.rb +132 -163
  33. data/lib/rspec/mocks/configuration.rb +28 -4
  34. data/lib/rspec/mocks/error_generator.rb +46 -13
  35. data/lib/rspec/mocks/example_methods.rb +13 -12
  36. data/lib/rspec/mocks/extensions/marshal.rb +1 -1
  37. data/lib/rspec/mocks/framework.rb +3 -4
  38. data/lib/rspec/mocks/instance_method_stasher.rb +2 -3
  39. data/lib/rspec/mocks/matchers/have_received.rb +8 -6
  40. data/lib/rspec/mocks/matchers/receive.rb +28 -20
  41. data/lib/rspec/mocks/matchers/receive_message_chain.rb +65 -0
  42. data/lib/rspec/mocks/matchers/receive_messages.rb +3 -2
  43. data/lib/rspec/mocks/message_chain.rb +91 -0
  44. data/lib/rspec/mocks/message_expectation.rb +86 -80
  45. data/lib/rspec/mocks/method_double.rb +2 -11
  46. data/lib/rspec/mocks/method_reference.rb +82 -23
  47. data/lib/rspec/mocks/method_signature_verifier.rb +207 -0
  48. data/lib/rspec/mocks/mutate_const.rb +34 -50
  49. data/lib/rspec/mocks/object_reference.rb +0 -1
  50. data/lib/rspec/mocks/proxy.rb +70 -13
  51. data/lib/rspec/mocks/ruby_features.rb +24 -0
  52. data/lib/rspec/mocks/space.rb +105 -31
  53. data/lib/rspec/mocks/standalone.rb +2 -2
  54. data/lib/rspec/mocks/syntax.rb +43 -8
  55. data/lib/rspec/mocks/targets.rb +16 -7
  56. data/lib/rspec/mocks/test_double.rb +41 -15
  57. data/lib/rspec/mocks/verifying_double.rb +51 -4
  58. data/lib/rspec/mocks/verifying_message_expecation.rb +12 -12
  59. data/lib/rspec/mocks/verifying_proxy.rb +32 -19
  60. data/lib/rspec/mocks/version.rb +1 -1
  61. data/spec/rspec/mocks/and_call_original_spec.rb +28 -7
  62. data/spec/rspec/mocks/and_return_spec.rb +23 -0
  63. data/spec/rspec/mocks/and_yield_spec.rb +1 -2
  64. data/spec/rspec/mocks/any_instance_spec.rb +33 -17
  65. data/spec/rspec/mocks/array_including_matcher_spec.rb +6 -6
  66. data/spec/rspec/mocks/before_all_spec.rb +132 -0
  67. data/spec/rspec/mocks/block_return_value_spec.rb +12 -1
  68. data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +9 -11
  69. data/spec/rspec/mocks/configuration_spec.rb +14 -1
  70. data/spec/rspec/mocks/double_spec.rb +867 -24
  71. data/spec/rspec/mocks/example_methods_spec.rb +13 -0
  72. data/spec/rspec/mocks/extensions/marshal_spec.rb +17 -17
  73. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +29 -1
  74. data/spec/rspec/mocks/hash_excluding_matcher_spec.rb +12 -12
  75. data/spec/rspec/mocks/hash_including_matcher_spec.rb +21 -17
  76. data/spec/rspec/mocks/instance_method_stasher_spec.rb +2 -3
  77. data/spec/rspec/mocks/matchers/have_received_spec.rb +7 -0
  78. data/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +198 -0
  79. data/spec/rspec/mocks/matchers/receive_messages_spec.rb +2 -2
  80. data/spec/rspec/mocks/matchers/receive_spec.rb +19 -6
  81. data/spec/rspec/mocks/method_signature_verifier_spec.rb +272 -0
  82. data/spec/rspec/mocks/methods_spec.rb +0 -1
  83. data/spec/rspec/mocks/multiple_return_value_spec.rb +2 -2
  84. data/spec/rspec/mocks/mutate_const_spec.rb +24 -1
  85. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +6 -22
  86. data/spec/rspec/mocks/null_object_mock_spec.rb +13 -7
  87. data/spec/rspec/mocks/options_hash_spec.rb +3 -3
  88. data/spec/rspec/mocks/{partial_mock_spec.rb → partial_double_spec.rb} +5 -2
  89. data/spec/rspec/mocks/{partial_mock_using_mocks_directly_spec.rb → partial_double_using_mocks_directly_spec.rb} +1 -1
  90. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +18 -0
  91. data/spec/rspec/mocks/serialization_spec.rb +1 -0
  92. data/spec/rspec/mocks/space_spec.rb +218 -7
  93. data/spec/rspec/mocks/stub_chain_spec.rb +6 -0
  94. data/spec/rspec/mocks/stub_spec.rb +0 -6
  95. data/spec/rspec/mocks/syntax_spec.rb +19 -0
  96. data/spec/rspec/mocks/test_double_spec.rb +0 -1
  97. data/spec/rspec/mocks/verifying_double_spec.rb +281 -18
  98. data/spec/rspec/mocks/verifying_message_expecation_spec.rb +7 -6
  99. data/spec/rspec/mocks_spec.rb +168 -42
  100. data/spec/spec_helper.rb +34 -22
  101. metadata +94 -63
  102. metadata.gz.sig +0 -0
  103. checksums.yaml +0 -15
  104. checksums.yaml.gz.sig +0 -2
  105. data/features/outside_rspec/configuration.feature +0 -60
  106. data/lib/rspec/mocks/arity_calculator.rb +0 -66
  107. data/lib/rspec/mocks/errors.rb +0 -12
  108. data/lib/rspec/mocks/mock.rb +0 -7
  109. data/lib/rspec/mocks/proxy_for_nil.rb +0 -37
  110. data/lib/rspec/mocks/stub_chain.rb +0 -51
  111. data/spec/rspec/mocks/argument_expectation_spec.rb +0 -32
  112. data/spec/rspec/mocks/arity_calculator_spec.rb +0 -95
  113. data/spec/rspec/mocks/mock_space_spec.rb +0 -113
  114. data/spec/rspec/mocks/mock_spec.rb +0 -788
@@ -69,6 +69,16 @@ module RSpec
69
69
  verify @double
70
70
  end
71
71
 
72
+ it 'allows unexpected message sends using `send`' do
73
+ val = @double.send(:foo).send(:bar)
74
+ expect(val).to equal(@double)
75
+ end
76
+
77
+ it 'allows unexpected message sends using `send`' do
78
+ val = @double.__send__(:foo).__send__(:bar)
79
+ expect(val).to equal(@double)
80
+ end
81
+
72
82
  it "allows expected message with different args first" do
73
83
  @double.should_receive(:message).with(:expected_arg)
74
84
  @double.message(:unexpected_arg)
@@ -89,15 +99,11 @@ module RSpec
89
99
  expect(("%i" % @double)).to eq("0")
90
100
  end
91
101
 
92
- it "does not allow null-ness to persist between examples" do
102
+ it "does not allow null objects to be used outside of examples" do
93
103
  RSpec::Mocks.teardown
94
104
 
95
- expect(@double).not_to be_null_object
96
- expect { @double.some.long.message.chain }.to raise_error(RSpec::Mocks::MockExpectationError)
97
-
98
- @double.as_null_object
99
- expect(@double).to be_null_object
100
- expect { @double.some.long.message.chain }.not_to raise_error
105
+ expect { @double.some.long.message.chain }.to raise_error(RSpec::Mocks::OutsideOfExampleError)
106
+ expect { @double.as_null_object }.to raise_error(RSpec::Mocks::OutsideOfExampleError)
101
107
  end
102
108
  end
103
109
 
@@ -5,7 +5,7 @@ module RSpec
5
5
  describe "calling :should_receive with an options hash" do
6
6
  it "reports the file and line submitted with :expected_from" do
7
7
  begin
8
- mock = RSpec::Mocks::Mock.new("a mock")
8
+ mock = RSpec::Mocks::Double.new("a mock")
9
9
  mock.should_receive(:message, :expected_from => "/path/to/blah.ext:37")
10
10
  verify mock
11
11
  rescue Exception => e
@@ -16,7 +16,7 @@ module RSpec
16
16
 
17
17
  it "uses the message supplied with :message" do
18
18
  expect {
19
- m = RSpec::Mocks::Mock.new("a mock")
19
+ m = RSpec::Mocks::Double.new("a mock")
20
20
  m.should_receive(:message, :message => "recebi nada")
21
21
  verify m
22
22
  }.to raise_error("recebi nada")
@@ -24,7 +24,7 @@ module RSpec
24
24
 
25
25
  it "uses the message supplied with :message after a similar stub" do
26
26
  expect {
27
- m = RSpec::Mocks::Mock.new("a mock")
27
+ m = RSpec::Mocks::Double.new("a mock")
28
28
  m.stub(:message)
29
29
  m.should_receive(:message, :message => "from mock")
30
30
  verify m
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  module RSpec
4
4
  module Mocks
5
- describe "using a Partial Mock," do
5
+ describe "A partial double" do
6
6
  let(:object) { Object.new }
7
7
 
8
8
  it "names the class in the failure message" do
@@ -298,7 +298,10 @@ module RSpec
298
298
  allow(object).to receive(:implemented)
299
299
  expect {
300
300
  object.implemented('bogus')
301
- }.to raise_error(ArgumentError, /wrong number of arguments \(1 for 0\)/)
301
+ }.to raise_error(
302
+ ArgumentError,
303
+ a_string_including("Wrong number of arguments. Expected 0, got 1.")
304
+ )
302
305
  end
303
306
  end
304
307
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module RSpec::Mocks
4
- describe "PartialMockUsingMocksDirectly" do
4
+ describe "PartialDoubleUsingMocksDirectly" do
5
5
  let(:klass) do
6
6
  Class.new do
7
7
  module MethodMissing
@@ -87,6 +87,14 @@ module RSpec
87
87
  @double.should_receive(:random_call).with(array_including(1,2))
88
88
  @double.random_call([1,2])
89
89
  end
90
+
91
+ it "matches any arbitrary object using #===" do
92
+ matcher = double
93
+ matcher.should_receive(:===).with(4).and_return(true)
94
+
95
+ @double.should_receive(:foo).with(matcher)
96
+ @double.foo(4)
97
+ end
90
98
  end
91
99
 
92
100
  context "handling non-matcher arguments" do
@@ -121,6 +129,16 @@ module RSpec
121
129
  @double.should_receive(:random_call).with(:a => "a", :b => "b")
122
130
  @double.random_call(opts)
123
131
  end
132
+
133
+ it "matches a class against itself" do
134
+ @double.should_receive(:foo).with(Fixnum)
135
+ @double.foo(Fixnum)
136
+ end
137
+
138
+ it "matches a class against an instance of itself" do
139
+ @double.should_receive(:foo).with(Fixnum)
140
+ @double.foo(3)
141
+ end
124
142
  end
125
143
  end
126
144
  end
@@ -61,6 +61,7 @@ module RSpec
61
61
  if compiled_with_psych
62
62
  context 'using Syck as the YAML engine' do
63
63
  before(:each) { ::YAML::ENGINE.yamler = 'syck' }
64
+ around(:each) { |example| with_isolated_stderr(&example) }
64
65
  it_behaves_like 'normal YAML serialization'
65
66
  end
66
67
 
@@ -2,16 +2,62 @@ require 'spec_helper'
2
2
 
3
3
  module RSpec::Mocks
4
4
  describe Space do
5
+ let(:space) { Space.new }
6
+ let(:dbl_1) { Object.new }
7
+ let(:dbl_2) { Object.new }
5
8
 
6
- describe "#proxies_of(klass)" do
7
- let(:space) { Space.new }
9
+ describe "#verify_all" do
10
+ it "verifies all mocks within" do
11
+ verifies = []
12
+
13
+ space.proxy_for(dbl_1).stub(:verify) { verifies << :dbl_1 }
14
+ space.proxy_for(dbl_2).stub(:verify) { verifies << :dbl_2 }
15
+
16
+ space.verify_all
17
+
18
+ expect(verifies).to match_array([:dbl_1, :dbl_2])
19
+ end
20
+
21
+ def define_singleton_method_on_recorder_for(klass, name, &block)
22
+ recorder = space.any_instance_recorder_for(klass)
23
+ (class << recorder; self; end).send(:define_method, name, &block)
24
+ end
25
+
26
+ it 'verifies all any_instance recorders within' do
27
+ klass_1, klass_2 = Class.new, Class.new
28
+
29
+ verifies = []
30
+
31
+ # We can't `stub` a method on the recorder because it defines its own `stub`...
32
+ define_singleton_method_on_recorder_for(klass_1, :verify) { verifies << :klass_1 }
33
+ define_singleton_method_on_recorder_for(klass_2, :verify) { verifies << :klass_2 }
34
+
35
+ space.verify_all
36
+
37
+ expect(verifies).to match_array([:klass_1, :klass_2])
38
+ end
39
+ end
40
+
41
+ describe "#reset_all" do
42
+ it "resets all mocks within" do
43
+ resets = []
44
+
45
+ space.proxy_for(dbl_1).stub(:reset) { resets << :dbl_1 }
46
+ space.proxy_for(dbl_2).stub(:reset) { resets << :dbl_2 }
47
+
48
+ space.reset_all
49
+
50
+ expect(resets).to match_array([:dbl_1, :dbl_2])
51
+ end
52
+ end
8
53
 
54
+ describe "#proxies_of(klass)" do
9
55
  it 'returns proxies' do
10
56
  space.proxy_for("")
11
- expect(space.proxies_of(String).map(&:class)).to eq([PartialMockProxy])
57
+ expect(space.proxies_of(String).map(&:class)).to eq([PartialDoubleProxy])
12
58
  end
13
59
 
14
- it 'returns only the proxies whose object is an instance of the given class' do
60
+ def create_generations
15
61
  grandparent_class = Class.new
16
62
  parent_class = Class.new(grandparent_class)
17
63
  child_class = Class.new(parent_class)
@@ -20,14 +66,179 @@ module RSpec::Mocks
20
66
  parent = parent_class.new
21
67
  child = child_class.new
22
68
 
69
+ return grandparent, parent, child
70
+ end
71
+
72
+ it 'returns only the proxies whose object is an instance of the given class' do
73
+ grandparent, parent, child = create_generations
74
+
23
75
  space.proxy_for(grandparent)
24
76
 
25
- parent_proxy = space.proxy_for(parent)
26
- child_proxy = space.proxy_for(child)
77
+ parent_proxy = space.proxy_for(parent)
78
+ child_proxy = space.proxy_for(child)
79
+
80
+ expect(space.proxies_of(parent.class)).to contain_exactly(parent_proxy, child_proxy)
81
+ end
82
+
83
+ it 'looks in the parent space for matching proxies' do
84
+ _, parent, child = create_generations
85
+
86
+ parent_proxy = space.proxy_for(parent)
87
+ subspace = space.new_scope
88
+ child_proxy = subspace.proxy_for(child)
89
+
90
+ expect(subspace.proxies_of(parent.class)).to contain_exactly(parent_proxy, child_proxy)
91
+ end
92
+ end
93
+
94
+ it 'tracks proxies in parent and child space separately' do
95
+ proxy1 = space.proxy_for(Object.new)
96
+ subspace = space.new_scope
97
+ proxy2 = subspace.proxy_for(Object.new)
98
+
99
+ expect(space.proxies.values).to include(proxy1)
100
+ expect(space.proxies.values).not_to include(proxy2)
101
+
102
+ expect(subspace.proxies.values).to include(proxy2)
103
+ expect(subspace.proxies.values).not_to include(proxy1)
104
+ end
105
+
106
+ it "only adds an instance once" do
107
+ m1 = double("mock1")
108
+
109
+ expect {
110
+ space.ensure_registered(m1)
111
+ }.to change { space.proxies }
112
+
113
+ expect {
114
+ space.ensure_registered(m1)
115
+ }.not_to change { space.proxies }
116
+ end
117
+
118
+ [:ensure_registered, :proxy_for].each do |method|
119
+ describe "##{method}" do
120
+ define_method :get_proxy do |space, object|
121
+ space.__send__(method, object)
122
+ end
123
+
124
+ it 'returns the proxy for the given object' do
125
+ obj1 = Object.new
126
+ obj2 = Object.new
127
+
128
+ expect(get_proxy(space, obj1)).to equal(get_proxy(space, obj1))
129
+ expect(get_proxy(space, obj2)).to equal(get_proxy(space, obj2))
130
+ expect(get_proxy(space, obj1)).not_to equal(get_proxy(space, obj2))
131
+ end
132
+
133
+ it 'can stil return a proxy from a parent context' do
134
+ proxy = get_proxy(space, Object)
135
+ subspace = space.new_scope
136
+
137
+ expect(get_proxy(subspace, Object)).to equal(proxy)
138
+ end
139
+
140
+ it "does not store a parent's proxy in the child space" do
141
+ get_proxy(space, Object)
142
+ subspace = space.new_scope
143
+
144
+ expect {
145
+ get_proxy(subspace, Object)
146
+ }.not_to change { subspace.proxies }.from({})
147
+ end
148
+ end
149
+ end
150
+
151
+ describe "#registered?" do
152
+ it 'returns true if registered in this space' do
153
+ space.ensure_registered(Object)
154
+ expect(space).to be_registered(Object)
155
+ end
156
+
157
+ it 'returns true if registered in a parent space' do
158
+ space.ensure_registered(Object)
159
+ expect(space.new_scope).to be_registered(Object)
160
+ end
161
+
162
+ it 'returns false if not registered in this or a parent space' do
163
+ expect(space.new_scope).not_to be_registered(Object)
164
+ end
165
+ end
166
+
167
+ describe "#constant_mutator_for" do
168
+ it 'returns the mutator for the given const name' do
169
+ space = RSpec::Mocks.space
170
+ stub_const("Foo", 3)
171
+ stub_const("Bar", 4)
172
+
173
+ expect(space.constant_mutator_for("Foo")).to equal(space.constant_mutator_for("Foo"))
174
+ expect(space.constant_mutator_for("Bar")).to equal(space.constant_mutator_for("Bar"))
175
+ expect(space.constant_mutator_for("Foo")).not_to equal(space.constant_mutator_for("Bar"))
176
+ end
27
177
 
28
- expect(space.proxies_of(parent_class)).to match_array([parent_proxy, child_proxy])
178
+ it 'can stil return a mutator from a parent context' do
179
+ space = RSpec::Mocks.space
180
+
181
+ stub_const("Foo", 3)
182
+ mutator = space.constant_mutator_for("Foo")
183
+
184
+ in_new_space_scope do
185
+ subspace = RSpec::Mocks.space
186
+ expect(subspace.constant_mutator_for("Foo")).to equal(mutator)
187
+ end
188
+ end
189
+ end
190
+
191
+ describe "#any_instance_recorder_for" do
192
+ it 'returns the recorder for the given class' do
193
+ expect(space.any_instance_recorder_for(String)).to equal(space.any_instance_recorder_for(String))
194
+ expect(space.any_instance_recorder_for(Symbol)).to equal(space.any_instance_recorder_for(Symbol))
195
+ expect(space.any_instance_recorder_for(String)).not_to equal(space.any_instance_recorder_for(Symbol))
196
+ end
197
+
198
+ it 'can stil return a recorder from a parent context' do
199
+ recorder = space.any_instance_recorder_for(String)
200
+ subspace = space.new_scope
201
+
202
+ expect(subspace.any_instance_recorder_for(String)).to equal(recorder)
203
+ end
204
+
205
+ it "does not store a parent's proxy in the child space" do
206
+ space.any_instance_recorder_for(String)
207
+ subspace = space.new_scope
208
+
209
+ expect {
210
+ subspace.any_instance_recorder_for(String)
211
+ }.not_to change { subspace.any_instance_recorders }.from({})
29
212
  end
30
213
  end
31
214
 
215
+ it 'can be diffed in a failure when it has references to an error generator via a proxy' do
216
+ space1 = Space.new
217
+ space2 = Space.new
218
+
219
+ space1.proxy_for("")
220
+ space2.proxy_for("")
221
+
222
+ expect {
223
+ expect(space1).to eq(space2)
224
+ }.to raise_error(RSpec::Expectations::ExpectationNotMetError, /Diff/)
225
+ end
226
+
227
+ it 'removes an any_instance_recorder when requested' do
228
+ klass = Class.new
229
+
230
+ space.any_instance_recorder_for(klass)
231
+
232
+ expect {
233
+ space.remove_any_instance_recorder_for(klass)
234
+ }.to change { space.any_instance_recorders.size }.by(-1)
235
+ end
236
+
237
+ def in_new_space_scope
238
+ RSpec::Mocks.setup
239
+ yield
240
+ ensure
241
+ RSpec::Mocks.teardown
242
+ end
32
243
  end
33
244
  end
@@ -37,6 +37,12 @@ module RSpec
37
37
  end
38
38
 
39
39
  context "with two methods in chain" do
40
+ it "accepts any number of arguments to the stubbed messages in the chain" do
41
+ object.stub_chain(:msg1, :msg2).and_return(:return_value)
42
+
43
+ expect(object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value")).to eq(:return_value)
44
+ end
45
+
40
46
  context "using and_return" do
41
47
  it "returns expected value from chaining two method calls" do
42
48
  object.stub_chain(:msg1, :msg2).and_return(:return_value)
@@ -263,12 +263,6 @@ module RSpec
263
263
  @stub.foo("bar")
264
264
  @stub.foo("baz")
265
265
  end
266
-
267
- it "calculates return value by executing block passed to #and_return" do
268
- @stub.stub(:something).with("a","b","c").and_return { |a,b,c| c+b+a }
269
- expect(@stub.something("a","b","c")).to eq "cba"
270
- verify @stub
271
- end
272
266
  end
273
267
 
274
268
  describe "A method stub with args" do
@@ -0,0 +1,19 @@
1
+ require "spec_helper"
2
+
3
+ module RSpec::Mocks
4
+ describe Syntax do
5
+ context "when the should syntax is enabled on a non-default syntax host" do
6
+ include_context "with the default mocks syntax"
7
+
8
+ it "continues to warn about the should syntax" do
9
+ my_host = Class.new
10
+ expect(RSpec).to receive(:deprecate)
11
+ Syntax.enable_should(my_host)
12
+
13
+ o = Object.new
14
+ o.should_receive(:bees)
15
+ o.bees
16
+ end
17
+ end
18
+ end
19
+ end
@@ -47,4 +47,3 @@ module RSpec
47
47
  end
48
48
  end
49
49
  end
50
-
@@ -1,28 +1,70 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  class LoadedClass
4
+ extend RSpec::Mocks::RubyFeatures
5
+
4
6
  M = :m
5
7
  N = :n
6
8
  INSTANCE = LoadedClass.new
7
9
 
8
- def defined_instance_method; end
9
- def self.defined_class_method; end
10
+ class << self
11
+
12
+ def respond_to?(method_name, include_all = false)
13
+ return true if method_name == :dynamic_class_method
14
+ super
15
+ end
16
+
17
+ def defined_class_method
18
+ end
19
+
20
+ def send
21
+ # fake out!
22
+ end
23
+
24
+ protected
25
+
26
+ def defined_protected_class_method
27
+ end
28
+
29
+ private
30
+
31
+ def defined_private_class_method
32
+ end
33
+
34
+ end
35
+
36
+ def defined_instance_method
37
+ end
10
38
 
11
- def respond_to?(method_name)
39
+ if required_kw_args_supported?
40
+ # Need to eval this since it is invalid syntax on earlier rubies.
41
+ eval <<-RUBY
42
+ def kw_args_method(optional_arg:'hello', required_arg:)
43
+ end
44
+ RUBY
45
+ end
46
+
47
+ def send(*)
48
+ end
49
+
50
+ def respond_to?(method_name, include_all = false)
12
51
  return true if method_name == :dynamic_instance_method
13
52
  super
14
53
  end
15
54
 
16
- def self.respond_to?(method_name)
17
- return true if method_name == :dynamic_class_method
18
- super
55
+ class Nested; end
56
+
57
+ protected
58
+
59
+ def defined_protected_method
19
60
  end
20
61
 
21
- def self.send
22
- # fake out!
62
+ private
63
+
64
+ def defined_private_method
65
+ "wink wink ;)"
23
66
  end
24
67
 
25
- class Nested; end
26
68
  end
27
69
 
28
70
  module RSpec
@@ -51,6 +93,27 @@ module RSpec
51
93
 
52
94
  expect(o.undefined_instance_method(:arg)).to eq(true)
53
95
  end
96
+
97
+ it 'handles classes that are materialized after mocking' do
98
+ stub_const "A::B", Object.new
99
+ o = instance_double "A", :undefined_instance_method => true
100
+
101
+ expect(o.undefined_instance_method).to eq(true)
102
+ end
103
+
104
+ context 'for null objects' do
105
+ let(:o) { instance_double('NonLoadedClass').as_null_object }
106
+
107
+ it 'returns self from any message' do
108
+ expect(o.a.b.c).to be(o)
109
+ end
110
+
111
+ it 'reports it responds to any message' do
112
+ expect(o.respond_to?(:a)).to be true
113
+ expect(o.respond_to?(:a, false)).to be true
114
+ expect(o.respond_to?(:a, true)).to be true
115
+ end
116
+ end
54
117
  end
55
118
 
56
119
  describe 'when doubled class is loaded' do
@@ -79,6 +142,64 @@ module RSpec
79
142
  prevents { o.should_receive(:defined_class_method) }
80
143
  end
81
144
 
145
+ it 'allows `send` to be stubbed if it is defined on the class' do
146
+ o = instance_double('LoadedClass')
147
+ allow(o).to receive(:send).and_return("received")
148
+ expect(o.send(:msg)).to eq("received")
149
+ end
150
+
151
+ it 'gives a descriptive error message for NoMethodError' do
152
+ o = instance_double("LoadedClass")
153
+ expect {
154
+ o.defined_private_method
155
+ }.to raise_error(NoMethodError,
156
+ /Double "LoadedClass \(instance\)"/)
157
+ end
158
+
159
+ describe "method visibility" do
160
+ shared_examples_for "preserves method visibility" do |visibility|
161
+ method_name = :"defined_#{visibility}_method"
162
+
163
+ it "can allow a #{visibility} instance method" do
164
+ o = instance_double('LoadedClass')
165
+ allow(o).to receive(method_name).and_return(3)
166
+ expect(o.send method_name).to eq(3)
167
+ end
168
+
169
+ it "can expect a #{visibility} instance method" do
170
+ o = instance_double('LoadedClass')
171
+ expect(o).to receive(method_name)
172
+ o.send method_name
173
+ end
174
+
175
+ it "preserves #{visibility} visibility when allowing a #{visibility} method" do
176
+ preserves_visibility(method_name, visibility) do
177
+ instance_double('LoadedClass').tap do |o|
178
+ allow(o).to receive(method_name)
179
+ end
180
+ end
181
+ end
182
+
183
+ it "preserves #{visibility} visibility when expecting a #{visibility} method" do
184
+ preserves_visibility(method_name, visibility) do
185
+ instance_double('LoadedClass').tap do |o|
186
+ expect(o).to receive(method_name).at_least(:once)
187
+ o.send(method_name) # to satisfy the expectation
188
+ end
189
+ end
190
+ end
191
+
192
+ it "preserves #{visibility} visibility on a null object" do
193
+ preserves_visibility(method_name, visibility) do
194
+ instance_double('LoadedClass').as_null_object
195
+ end
196
+ end
197
+ end
198
+
199
+ include_examples "preserves method visibility", :private
200
+ include_examples "preserves method visibility", :protected
201
+ end
202
+
82
203
  it 'does not allow dynamic methods to be expected' do
83
204
  # This isn't possible at the moment since an instance of the class
84
205
  # would be required for the verification, and we only have the
@@ -103,7 +224,23 @@ module RSpec
103
224
  o = instance_double('LoadedClass', :defined_instance_method => 25)
104
225
  expect {
105
226
  o.defined_instance_method(:a)
106
- }.to raise_error(ArgumentError)
227
+ }.to raise_error(ArgumentError,
228
+ "Wrong number of arguments. Expected 0, got 1.")
229
+ end
230
+
231
+ if required_kw_args_supported?
232
+ it 'allows keyword arguments' do
233
+ o = instance_double('LoadedClass', :kw_args_method => true)
234
+ expect(o.kw_args_method(:required_arg => 'something')).to eq(true)
235
+ end
236
+
237
+ it 'checks that stubbed methods with required keyword args are ' +
238
+ 'invoked with the required arguments' do
239
+ o = instance_double('LoadedClass', :kw_args_method => true)
240
+ expect {
241
+ o.kw_args_method(:optional_arg => 'something')
242
+ }.to raise_error(ArgumentError)
243
+ end
107
244
  end
108
245
 
109
246
  it 'allows class to be specified by constant' do
@@ -111,11 +248,45 @@ module RSpec
111
248
  expect(o.defined_instance_method).to eq(1)
112
249
  end
113
250
 
114
- it 'only allows defined methods for null objects' do
115
- o = instance_double('LoadedClass').as_null_object
116
-
117
- expect(o.defined_instance_method).to eq(o)
118
- prevents { o.undefined_method }
251
+ context 'for null objects' do
252
+ let(:o) { instance_double('LoadedClass').as_null_object }
253
+
254
+ it 'only allows defined methods' do
255
+ expect(o.defined_instance_method).to eq(o)
256
+ prevents { o.undefined_method }
257
+ prevents { o.send(:undefined_method) }
258
+ prevents { o.__send__(:undefined_method) }
259
+ end
260
+
261
+ it 'reports what public messages it responds to accurately' do
262
+ expect(o.respond_to?(:defined_instance_method)).to be true
263
+ expect(o.respond_to?(:defined_instance_method, true)).to be true
264
+ expect(o.respond_to?(:defined_instance_method, false)).to be true
265
+
266
+ expect(o.respond_to?(:undefined_method)).to be false
267
+ expect(o.respond_to?(:undefined_method, true)).to be false
268
+ expect(o.respond_to?(:undefined_method, false)).to be false
269
+ end
270
+
271
+ it 'reports that it responds to defined private methods when the appropriate arg is passed' do
272
+ expect(o.respond_to?(:defined_private_method)).to be false
273
+ expect(o.respond_to?(:defined_private_method, true)).to be true
274
+ expect(o.respond_to?(:defined_private_method, false)).to be false
275
+ end
276
+
277
+ if RUBY_VERSION.to_f < 2.0 # respond_to?(:protected_method) changed behavior in Ruby 2.0.
278
+ it 'reports that it responds to protected methods' do
279
+ expect(o.respond_to?(:defined_protected_method)).to be true
280
+ expect(o.respond_to?(:defined_protected_method, true)).to be true
281
+ expect(o.respond_to?(:defined_protected_method, false)).to be true
282
+ end
283
+ else
284
+ it 'reports that it responds to protected methods when the appropriate arg is passed' do
285
+ expect(o.respond_to?(:defined_protected_method)).to be false
286
+ expect(o.respond_to?(:defined_protected_method, true)).to be true
287
+ expect(o.respond_to?(:defined_protected_method, false)).to be false
288
+ end
289
+ end
119
290
  end
120
291
  end
121
292
 
@@ -173,6 +344,57 @@ module RSpec
173
344
  prevents { o.should_receive(:defined_instance_method) }
174
345
  end
175
346
 
347
+ it 'gives a descriptive error message for NoMethodError' do
348
+ o = class_double("LoadedClass")
349
+ expect {
350
+ o.defined_private_class_method
351
+ }.to raise_error(NoMethodError, /Double "LoadedClass"/)
352
+ end
353
+
354
+ describe "method visibility" do
355
+ shared_examples_for "preserves method visibility" do |visibility|
356
+ method_name = :"defined_#{visibility}_class_method"
357
+
358
+ it "can allow a #{visibility} instance method" do
359
+ o = class_double('LoadedClass')
360
+ allow(o).to receive(method_name).and_return(3)
361
+ expect(o.send method_name).to eq(3)
362
+ end
363
+
364
+ it "can expect a #{visibility} instance method" do
365
+ o = class_double('LoadedClass')
366
+ expect(o).to receive(method_name)
367
+ o.send method_name
368
+ end
369
+
370
+ it "preserves #{visibility} visibility when allowing a #{visibility} method" do
371
+ preserves_visibility(method_name, visibility) do
372
+ class_double('LoadedClass').tap do |o|
373
+ allow(o).to receive(method_name)
374
+ end
375
+ end
376
+ end
377
+
378
+ it "preserves #{visibility} visibility when expecting a #{visibility} method" do
379
+ preserves_visibility(method_name, visibility) do
380
+ class_double('LoadedClass').tap do |o|
381
+ expect(o).to receive(method_name).at_least(:once)
382
+ o.send(method_name) # to satisfy the expectation
383
+ end
384
+ end
385
+ end
386
+
387
+ it "preserves #{visibility} visibility on a null object" do
388
+ preserves_visibility(method_name, visibility) do
389
+ class_double('LoadedClass').as_null_object
390
+ end
391
+ end
392
+ end
393
+
394
+ include_examples "preserves method visibility", :private
395
+ include_examples "preserves method visibility", :protected
396
+ end
397
+
176
398
  it 'checks that stubbed methods are invoked with the correct arity' do
177
399
  o = class_double('LoadedClass', :defined_class_method => 1)
178
400
  expect {
@@ -210,7 +432,8 @@ module RSpec
210
432
  expect(object).to receive(:defined_class_method)
211
433
 
212
434
  expect(LoadedClass).to eq(object)
213
- ::RSpec::Mocks.space.reset_all
435
+ ::RSpec::Mocks.teardown
436
+ ::RSpec::Mocks.setup
214
437
  expect(LoadedClass).to eq(original)
215
438
  end
216
439
 
@@ -243,6 +466,14 @@ module RSpec
243
466
  class_double(Object.new)
244
467
  }.to raise_error(/Module or String expected/)
245
468
  end
469
+
470
+ it "trying to raise a class_double raises a TypeError", :unless => RUBY_VERSION == '1.9.2' do
471
+ subject = Object.new
472
+ class_double("StubbedError").as_stubbed_const
473
+ allow(subject).to receive(:some_method).and_raise(StubbedError)
474
+ expect { subject.some_method }.to raise_error(TypeError, 'exception class/object expected')
475
+ end
476
+
246
477
  end
247
478
 
248
479
  describe 'object doubles' do
@@ -266,6 +497,8 @@ module RSpec
266
497
 
267
498
  expect(o).to receive(:defined_instance_method)
268
499
  o.defined_instance_method
500
+ expect(o).to receive(:defined_private_method)
501
+ o.send :defined_private_method
269
502
  end
270
503
 
271
504
  it 'can create a double that matches the interface of any arbitrary object' do
@@ -277,6 +510,8 @@ module RSpec
277
510
 
278
511
  expect(o).to receive(:defined_instance_method)
279
512
  o.defined_instance_method
513
+ expect(o).to receive(:defined_private_method)
514
+ o.send :defined_private_method
280
515
  end
281
516
 
282
517
  it 'does not allow transferring constants to an object' do
@@ -308,13 +543,13 @@ module RSpec
308
543
  it 'prevents creation of instance doubles for unloaded constants' do
309
544
  expect {
310
545
  instance_double('LoadedClas')
311
- }.to raise_error(NameError)
546
+ }.to raise_error(VerifyingDoubleNotDefinedError)
312
547
  end
313
548
 
314
549
  it 'prevents creation of class doubles for unloaded constants' do
315
550
  expect {
316
551
  class_double('LoadedClas')
317
- }.to raise_error(NameError)
552
+ }.to raise_error(VerifyingDoubleNotDefinedError)
318
553
  end
319
554
  end
320
555
 
@@ -322,6 +557,34 @@ module RSpec
322
557
  expect { instance_double(1) }.to raise_error(ArgumentError)
323
558
  expect { instance_double(nil) }.to raise_error(ArgumentError)
324
559
  end
560
+
561
+ def preserves_visibility(method_name, visibility)
562
+ double = yield
563
+
564
+ expect {
565
+ # send bypasses visbility, so we use eval instead.
566
+ eval("double.#{method_name}")
567
+ }.to raise_error(NoMethodError, a_message_indicating_visibility_violation(method_name, visibility))
568
+
569
+ expect { double.send(method_name) }.not_to raise_error
570
+ expect { double.__send__(method_name) }.not_to raise_error
571
+
572
+ unless double.null_object?
573
+ # Null object doubles use `method_missing` and so the singleton class
574
+ # doesn't know what methods are defined.
575
+ singleton_class = class << double; self; end
576
+ expect(singleton_class.send("#{visibility}_method_defined?", method_name)).to be true
577
+ end
578
+ end
579
+
580
+ RSpec::Matchers.define :a_message_indicating_visibility_violation do |method_name, visibility|
581
+ match do |msg|
582
+ # This should NOT Be just `msg.match(visibility)` because the method being called
583
+ # has the visibility name in it. We want to ensure it's a message that ruby is
584
+ # stating is of the given visibility.
585
+ msg.match("#{visibility} ") && msg.match(method_name.to_s)
586
+ end
587
+ end
325
588
  end
326
589
  end
327
590
  end