rspec-mocks 3.0.0.beta1 → 3.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +1 -1
- data/Changelog.md +95 -3
- data/README.md +27 -13
- data/features/README.md +15 -7
- data/features/argument_matchers/README.md +5 -5
- data/features/argument_matchers/explicit.feature +6 -6
- data/features/argument_matchers/general_matchers.feature +4 -4
- data/features/argument_matchers/type_matchers.feature +2 -2
- data/features/message_expectations/README.md +29 -27
- data/features/message_expectations/call_original.feature +0 -1
- data/features/message_expectations/message_chains_using_expect.feature +49 -0
- data/features/method_stubs/README.md +2 -1
- data/features/method_stubs/{any_instance.feature → allow_any_instance_of.feature} +12 -12
- data/features/method_stubs/{stub_chain.feature → receive_message_chain.feature} +3 -3
- data/features/method_stubs/to_ary.feature +1 -1
- data/features/mutating_constants/stub_defined_constant.feature +0 -1
- data/features/outside_rspec/standalone.feature +1 -1
- data/features/spies/spy_pure_mock_method.feature +1 -1
- data/features/test_frameworks/test_unit.feature +21 -10
- data/features/verifying_doubles/README.md +17 -0
- data/features/verifying_doubles/class_doubles.feature +1 -16
- data/features/verifying_doubles/dynamic_classes.feature +0 -1
- data/features/verifying_doubles/{introduction.feature → instance_doubles.feature} +41 -23
- data/features/verifying_doubles/partial_doubles.feature +2 -2
- data/lib/rspec/mocks.rb +69 -82
- data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +35 -0
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +1 -2
- data/lib/rspec/mocks/any_instance/recorder.rb +52 -18
- data/lib/rspec/mocks/any_instance/stub_chain.rb +1 -1
- data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +4 -0
- data/lib/rspec/mocks/argument_list_matcher.rb +10 -44
- data/lib/rspec/mocks/argument_matchers.rb +132 -163
- data/lib/rspec/mocks/configuration.rb +28 -4
- data/lib/rspec/mocks/error_generator.rb +46 -13
- data/lib/rspec/mocks/example_methods.rb +13 -12
- data/lib/rspec/mocks/extensions/marshal.rb +1 -1
- data/lib/rspec/mocks/framework.rb +3 -4
- data/lib/rspec/mocks/instance_method_stasher.rb +2 -3
- data/lib/rspec/mocks/matchers/have_received.rb +8 -6
- data/lib/rspec/mocks/matchers/receive.rb +28 -20
- data/lib/rspec/mocks/matchers/receive_message_chain.rb +65 -0
- data/lib/rspec/mocks/matchers/receive_messages.rb +3 -2
- data/lib/rspec/mocks/message_chain.rb +91 -0
- data/lib/rspec/mocks/message_expectation.rb +86 -80
- data/lib/rspec/mocks/method_double.rb +2 -11
- data/lib/rspec/mocks/method_reference.rb +82 -23
- data/lib/rspec/mocks/method_signature_verifier.rb +207 -0
- data/lib/rspec/mocks/mutate_const.rb +34 -50
- data/lib/rspec/mocks/object_reference.rb +0 -1
- data/lib/rspec/mocks/proxy.rb +70 -13
- data/lib/rspec/mocks/ruby_features.rb +24 -0
- data/lib/rspec/mocks/space.rb +105 -31
- data/lib/rspec/mocks/standalone.rb +2 -2
- data/lib/rspec/mocks/syntax.rb +43 -8
- data/lib/rspec/mocks/targets.rb +16 -7
- data/lib/rspec/mocks/test_double.rb +41 -15
- data/lib/rspec/mocks/verifying_double.rb +51 -4
- data/lib/rspec/mocks/verifying_message_expecation.rb +12 -12
- data/lib/rspec/mocks/verifying_proxy.rb +32 -19
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/and_call_original_spec.rb +28 -7
- data/spec/rspec/mocks/and_return_spec.rb +23 -0
- data/spec/rspec/mocks/and_yield_spec.rb +1 -2
- data/spec/rspec/mocks/any_instance_spec.rb +33 -17
- data/spec/rspec/mocks/array_including_matcher_spec.rb +6 -6
- data/spec/rspec/mocks/before_all_spec.rb +132 -0
- data/spec/rspec/mocks/block_return_value_spec.rb +12 -1
- data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +9 -11
- data/spec/rspec/mocks/configuration_spec.rb +14 -1
- data/spec/rspec/mocks/double_spec.rb +867 -24
- data/spec/rspec/mocks/example_methods_spec.rb +13 -0
- data/spec/rspec/mocks/extensions/marshal_spec.rb +17 -17
- data/spec/rspec/mocks/failing_argument_matchers_spec.rb +29 -1
- data/spec/rspec/mocks/hash_excluding_matcher_spec.rb +12 -12
- data/spec/rspec/mocks/hash_including_matcher_spec.rb +21 -17
- data/spec/rspec/mocks/instance_method_stasher_spec.rb +2 -3
- data/spec/rspec/mocks/matchers/have_received_spec.rb +7 -0
- data/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +198 -0
- data/spec/rspec/mocks/matchers/receive_messages_spec.rb +2 -2
- data/spec/rspec/mocks/matchers/receive_spec.rb +19 -6
- data/spec/rspec/mocks/method_signature_verifier_spec.rb +272 -0
- data/spec/rspec/mocks/methods_spec.rb +0 -1
- data/spec/rspec/mocks/multiple_return_value_spec.rb +2 -2
- data/spec/rspec/mocks/mutate_const_spec.rb +24 -1
- data/spec/rspec/mocks/nil_expectation_warning_spec.rb +6 -22
- data/spec/rspec/mocks/null_object_mock_spec.rb +13 -7
- data/spec/rspec/mocks/options_hash_spec.rb +3 -3
- data/spec/rspec/mocks/{partial_mock_spec.rb → partial_double_spec.rb} +5 -2
- data/spec/rspec/mocks/{partial_mock_using_mocks_directly_spec.rb → partial_double_using_mocks_directly_spec.rb} +1 -1
- data/spec/rspec/mocks/passing_argument_matchers_spec.rb +18 -0
- data/spec/rspec/mocks/serialization_spec.rb +1 -0
- data/spec/rspec/mocks/space_spec.rb +218 -7
- data/spec/rspec/mocks/stub_chain_spec.rb +6 -0
- data/spec/rspec/mocks/stub_spec.rb +0 -6
- data/spec/rspec/mocks/syntax_spec.rb +19 -0
- data/spec/rspec/mocks/test_double_spec.rb +0 -1
- data/spec/rspec/mocks/verifying_double_spec.rb +281 -18
- data/spec/rspec/mocks/verifying_message_expecation_spec.rb +7 -6
- data/spec/rspec/mocks_spec.rb +168 -42
- data/spec/spec_helper.rb +34 -22
- metadata +94 -63
- metadata.gz.sig +0 -0
- checksums.yaml +0 -15
- checksums.yaml.gz.sig +0 -2
- data/features/outside_rspec/configuration.feature +0 -60
- data/lib/rspec/mocks/arity_calculator.rb +0 -66
- data/lib/rspec/mocks/errors.rb +0 -12
- data/lib/rspec/mocks/mock.rb +0 -7
- data/lib/rspec/mocks/proxy_for_nil.rb +0 -37
- data/lib/rspec/mocks/stub_chain.rb +0 -51
- data/spec/rspec/mocks/argument_expectation_spec.rb +0 -32
- data/spec/rspec/mocks/arity_calculator_spec.rb +0 -95
- data/spec/rspec/mocks/mock_space_spec.rb +0 -113
- 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
|
102
|
+
it "does not allow null objects to be used outside of examples" do
|
93
103
|
RSpec::Mocks.teardown
|
94
104
|
|
95
|
-
expect
|
96
|
-
expect { @double.
|
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::
|
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::
|
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::
|
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 "
|
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(
|
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
|
@@ -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
|
@@ -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 "#
|
7
|
-
|
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([
|
57
|
+
expect(space.proxies_of(String).map(&:class)).to eq([PartialDoubleProxy])
|
12
58
|
end
|
13
59
|
|
14
|
-
|
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
|
26
|
-
child_proxy
|
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
|
-
|
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
|
@@ -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
|
-
|
9
|
-
|
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
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
55
|
+
class Nested; end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def defined_protected_method
|
19
60
|
end
|
20
61
|
|
21
|
-
|
22
|
-
|
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
|
-
|
115
|
-
o
|
116
|
-
|
117
|
-
|
118
|
-
|
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.
|
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(
|
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(
|
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
|