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.
- 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
|