rspec-mocks 2.14.0.rc1 → 2.14.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +25 -0
- data/features/message_expectations/allow_any_instance_of.feature +26 -0
- data/features/message_expectations/expect_any_instance_of.feature +27 -0
- data/features/message_expectations/expect_message_using_expect.feature +4 -0
- data/features/method_stubs/README.md +28 -2
- data/features/method_stubs/any_instance.feature +5 -1
- data/features/method_stubs/as_null_object.feature +6 -1
- data/features/method_stubs/simple_return_value_with_allow.feature +44 -0
- data/features/method_stubs/{simple_return_value.feature → simple_return_value_with_stub.feature} +0 -0
- data/features/method_stubs/stub_implementation.feature +23 -1
- data/lib/rspec/mocks.rb +35 -0
- data/lib/rspec/mocks/any_instance/chain.rb +27 -18
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +1 -28
- data/lib/rspec/mocks/any_instance/recorder.rb +1 -2
- data/lib/rspec/mocks/any_instance/stub_chain.rb +2 -1
- data/lib/rspec/mocks/error_generator.rb +7 -0
- data/lib/rspec/mocks/extensions/marshal.rb +7 -7
- data/lib/rspec/mocks/matchers/receive.rb +5 -1
- data/lib/rspec/mocks/message_expectation.rb +142 -63
- data/lib/rspec/mocks/method_double.rb +0 -9
- data/lib/rspec/mocks/proxy.rb +0 -5
- data/lib/rspec/mocks/proxy_for_nil.rb +2 -1
- data/lib/rspec/mocks/syntax.rb +15 -5
- data/lib/rspec/mocks/test_double.rb +2 -2
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/any_instance_spec.rb +26 -0
- data/spec/rspec/mocks/block_return_value_spec.rb +18 -0
- data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +205 -0
- data/spec/rspec/mocks/extensions/marshal_spec.rb +54 -0
- data/spec/rspec/mocks/matchers/receive_spec.rb +10 -0
- data/spec/rspec/mocks/mock_spec.rb +31 -3
- data/spec/rspec/mocks/partial_mock_spec.rb +3 -3
- data/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +81 -0
- data/spec/rspec/mocks/test_double_spec.rb +16 -6
- metadata +24 -9
data/lib/rspec/mocks/proxy.rb
CHANGED
@@ -51,11 +51,6 @@ module RSpec
|
|
51
51
|
meth_double.add_expectation @error_generator, @expectation_ordering, location, opts, &block
|
52
52
|
end
|
53
53
|
|
54
|
-
# @private
|
55
|
-
def add_negative_message_expectation(location, method_name, &implementation)
|
56
|
-
method_double[method_name].add_negative_expectation @error_generator, @expectation_ordering, location, &implementation
|
57
|
-
end
|
58
|
-
|
59
54
|
# @private
|
60
55
|
def build_expectation(method_name)
|
61
56
|
meth_double = method_double[method_name]
|
@@ -28,7 +28,8 @@ module RSpec
|
|
28
28
|
private
|
29
29
|
|
30
30
|
def warn method_name
|
31
|
-
|
31
|
+
non_rspec_caller = caller.find { |line| !line.include?('lib/rspec/mocks') }
|
32
|
+
Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{non_rspec_caller}. Use allow_message_expectations_on_nil to disable warnings.")
|
32
33
|
end
|
33
34
|
|
34
35
|
end
|
data/lib/rspec/mocks/syntax.rb
CHANGED
@@ -12,20 +12,21 @@ module RSpec
|
|
12
12
|
|
13
13
|
syntax_host.class_eval do
|
14
14
|
def should_receive(message, opts={}, &block)
|
15
|
-
|
16
|
-
|
15
|
+
opts[:expected_from] ||= caller(1)[0]
|
16
|
+
::RSpec::Mocks.expect_message(self, message.to_sym, opts, &block)
|
17
17
|
end
|
18
18
|
|
19
19
|
def should_not_receive(message, &block)
|
20
|
-
|
21
|
-
|
20
|
+
opts = {:expected_from => caller(1)[0]}
|
21
|
+
::RSpec::Mocks.expect_message(self, message.to_sym, opts, &block).never
|
22
22
|
end
|
23
23
|
|
24
24
|
def stub(message_or_hash, opts={}, &block)
|
25
25
|
if ::Hash === message_or_hash
|
26
26
|
message_or_hash.each {|message, value| stub(message).and_return value }
|
27
27
|
else
|
28
|
-
|
28
|
+
opts[:expected_from] = caller(1)[0]
|
29
|
+
::RSpec::Mocks.allow_message(self, message_or_hash, opts, &block)
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
@@ -155,6 +156,15 @@ module RSpec
|
|
155
156
|
# @api private
|
156
157
|
# Determines where the methods like `should_receive`, and `stub` are added.
|
157
158
|
def self.default_should_syntax_host
|
159
|
+
# JRuby 1.7.4 introduces a regression whereby `defined?(::BasicObject) => nil`
|
160
|
+
# yet `BasicObject` still exists and patching onto ::Object breaks things
|
161
|
+
# e.g. SimpleDelegator expectations won't work
|
162
|
+
#
|
163
|
+
# See: https://github.com/jruby/jruby/issues/814
|
164
|
+
if defined?(JRUBY_VERSION) && JRUBY_VERSION == '1.7.4' && RUBY_VERSION.to_f > 1.8
|
165
|
+
return ::BasicObject
|
166
|
+
end
|
167
|
+
|
158
168
|
# On 1.8.7, Object.ancestors.last == Kernel but
|
159
169
|
# things blow up if we include `RSpec::Mocks::Methods`
|
160
170
|
# into Kernel...not sure why.
|
@@ -62,7 +62,7 @@ module RSpec
|
|
62
62
|
|
63
63
|
# @private
|
64
64
|
def __build_mock_proxy
|
65
|
-
Proxy.new(self, @name, @options)
|
65
|
+
Proxy.new(self, @name, @options || {})
|
66
66
|
end
|
67
67
|
|
68
68
|
private
|
@@ -110,7 +110,7 @@ module RSpec
|
|
110
110
|
|
111
111
|
def assign_stubs(stubs)
|
112
112
|
stubs.each_pair do |message, response|
|
113
|
-
|
113
|
+
Mocks.allow_message(self, message).and_return(response)
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
data/lib/rspec/mocks/version.rb
CHANGED
@@ -181,6 +181,26 @@ module RSpec
|
|
181
181
|
end
|
182
182
|
end
|
183
183
|
|
184
|
+
context 'with #and_call_original and competing #with' do
|
185
|
+
let(:klass) { Struct.new(:a_method) }
|
186
|
+
|
187
|
+
it 'can combine and_call_original, with, and_return' do
|
188
|
+
allow_any_instance_of(klass).to receive(:a_method).and_call_original
|
189
|
+
allow_any_instance_of(klass).to receive(:a_method).with(:arg).and_return('value')
|
190
|
+
|
191
|
+
expect(klass.new('org').a_method).to eq 'org'
|
192
|
+
expect(klass.new.a_method(:arg)).to eq 'value'
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'can combine and_call_original, with, and_return (old syntax)' do
|
196
|
+
klass.any_instance.stub(:a_method).and_call_original
|
197
|
+
klass.any_instance.stub(:a_method).with(:arg).and_return('value')
|
198
|
+
|
199
|
+
expect(klass.new('org').a_method).to eq 'org'
|
200
|
+
expect(klass.new.a_method(:arg)).to eq 'value'
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
184
204
|
context "with #and_raise" do
|
185
205
|
it "stubs a method that doesn't exist" do
|
186
206
|
klass.any_instance.stub(:foo).and_raise(CustomErrorForAnyInstanceSpec)
|
@@ -327,6 +347,12 @@ module RSpec
|
|
327
347
|
RSpec::Mocks.space.verify_all
|
328
348
|
end
|
329
349
|
end
|
350
|
+
|
351
|
+
it "prevents confusing double-negative expressions involving `never`" do
|
352
|
+
expect {
|
353
|
+
klass.any_instance.should_not_receive(:not_expected).never
|
354
|
+
}.to raise_error(/trying to negate it again/)
|
355
|
+
end
|
330
356
|
end
|
331
357
|
|
332
358
|
context "with #should_receive" do
|
@@ -23,6 +23,12 @@ describe "a double declaration with a block handed to:" do
|
|
23
23
|
obj.stub(:foo) { 'bar' }
|
24
24
|
expect(obj.foo).to eq('bar')
|
25
25
|
end
|
26
|
+
|
27
|
+
it "does not complain if a lambda block and mismatched arguments are passed" do
|
28
|
+
obj = Object.new
|
29
|
+
obj.stub(:foo, &lambda { 'bar' })
|
30
|
+
expect(obj.foo(1, 2)).to eq('bar')
|
31
|
+
end
|
26
32
|
end
|
27
33
|
|
28
34
|
describe "with" do
|
@@ -31,6 +37,12 @@ describe "a double declaration with a block handed to:" do
|
|
31
37
|
obj.stub(:foo).with('baz') { 'bar' }
|
32
38
|
expect(obj.foo('baz')).to eq('bar')
|
33
39
|
end
|
40
|
+
|
41
|
+
it "does not complain if a lambda block and mismatched arguments are passed" do
|
42
|
+
obj = Object.new
|
43
|
+
obj.stub(:foo).with(1, 2, &lambda { 'bar' })
|
44
|
+
expect(obj.foo(1, 2)).to eq('bar')
|
45
|
+
end
|
34
46
|
end
|
35
47
|
|
36
48
|
%w[once twice ordered and_return].each do |method|
|
@@ -40,6 +52,12 @@ describe "a double declaration with a block handed to:" do
|
|
40
52
|
obj.stub(:foo).send(method) { 'bar' }
|
41
53
|
expect(obj.foo).to eq('bar')
|
42
54
|
end
|
55
|
+
|
56
|
+
it "does not complain if a lambda block and mismatched arguments are passed" do
|
57
|
+
obj = Object.new
|
58
|
+
obj.stub(:foo).send(method, &lambda { 'bar' })
|
59
|
+
expect(obj.foo(1, 2)).to eq('bar')
|
60
|
+
end
|
43
61
|
end
|
44
62
|
end
|
45
63
|
|
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Mocks
|
5
|
+
describe "Combining implementation instructions" do
|
6
|
+
it 'can combine and_yield and and_return' do
|
7
|
+
dbl = double
|
8
|
+
dbl.stub(:foo).and_yield(5).and_return(3)
|
9
|
+
|
10
|
+
expect { |b|
|
11
|
+
expect(dbl.foo(&b)).to eq(3)
|
12
|
+
}.to yield_with_args(5)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "combining and_yield, a block implementation and and_return" do
|
16
|
+
def verify_combined_implementation
|
17
|
+
dbl = double
|
18
|
+
(yield dbl).and_yield(5).and_return(3)
|
19
|
+
|
20
|
+
expect { |b|
|
21
|
+
expect(dbl.foo(:arg, &b)).to eq(3)
|
22
|
+
}.to yield_with_args(5)
|
23
|
+
|
24
|
+
expect(@block_called).to be_true
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'works when passing a block to `stub`' do
|
28
|
+
verify_combined_implementation do |dbl|
|
29
|
+
dbl.stub(:foo) { @block_called = true }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'works when passing a block to `with`' do
|
34
|
+
verify_combined_implementation do |dbl|
|
35
|
+
dbl.stub(:foo).with(:arg) { @block_called = true }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'works when passing a block to `exactly`' do
|
40
|
+
verify_combined_implementation do |dbl|
|
41
|
+
dbl.should_receive(:foo).exactly(:once) { @block_called = true }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'works when passing a block to `at_least`' do
|
46
|
+
verify_combined_implementation do |dbl|
|
47
|
+
dbl.should_receive(:foo).at_least(:once) { @block_called = true }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'works when passing a block to `at_most`' do
|
52
|
+
verify_combined_implementation do |dbl|
|
53
|
+
dbl.should_receive(:foo).at_most(:once) { @block_called = true }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'works when passing a block to `times`' do
|
58
|
+
verify_combined_implementation do |dbl|
|
59
|
+
dbl.should_receive(:foo).exactly(1).times { @block_called = true }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'works when passing a block to `any_number_of_times`' do
|
64
|
+
verify_combined_implementation do |dbl|
|
65
|
+
dbl.should_receive(:foo).any_number_of_times { @block_called = true }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'works when passing a block to `once`' do
|
70
|
+
verify_combined_implementation do |dbl|
|
71
|
+
dbl.should_receive(:foo).once { @block_called = true }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'works when passing a block to `twice`' do
|
76
|
+
the_double = nil
|
77
|
+
|
78
|
+
verify_combined_implementation do |dbl|
|
79
|
+
the_double = dbl
|
80
|
+
dbl.should_receive(:foo).twice { @block_called = true }
|
81
|
+
end
|
82
|
+
|
83
|
+
the_double.foo { |a| } # to ensure it is called twice
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'works when passing a block to `ordered`' do
|
87
|
+
verify_combined_implementation do |dbl|
|
88
|
+
dbl.should_receive(:foo).ordered { @block_called = true }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'can combine and_yield and and_return with a block' do
|
94
|
+
dbl = double
|
95
|
+
dbl.stub(:foo).and_yield(5).and_return { :return }
|
96
|
+
|
97
|
+
expect { |b|
|
98
|
+
expect(dbl.foo(&b)).to eq(:return)
|
99
|
+
}.to yield_with_args(5)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'can combine and_yield and and_raise' do
|
103
|
+
dbl = double
|
104
|
+
dbl.stub(:foo).and_yield(5).and_raise("boom")
|
105
|
+
|
106
|
+
expect { |b|
|
107
|
+
expect { dbl.foo(&b) }.to raise_error("boom")
|
108
|
+
}.to yield_with_args(5)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'can combine and_yield, a block implementation and and_raise' do
|
112
|
+
dbl = double
|
113
|
+
block_called = false
|
114
|
+
dbl.stub(:foo) { block_called = true }.and_yield(5).and_raise("boom")
|
115
|
+
|
116
|
+
expect { |b|
|
117
|
+
expect { dbl.foo(&b) }.to raise_error("boom")
|
118
|
+
}.to yield_with_args(5)
|
119
|
+
|
120
|
+
expect(block_called).to be_true
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'can combine and_yield and and_throw' do
|
124
|
+
dbl = double
|
125
|
+
dbl.stub(:foo).and_yield(5).and_throw(:bar)
|
126
|
+
|
127
|
+
expect { |b|
|
128
|
+
expect { dbl.foo(&b) }.to throw_symbol(:bar)
|
129
|
+
}.to yield_with_args(5)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'can combine and_yield, a block implementation and and_throw' do
|
133
|
+
dbl = double
|
134
|
+
block_called = false
|
135
|
+
dbl.stub(:foo) { block_called = true }.and_yield(5).and_throw(:bar)
|
136
|
+
|
137
|
+
expect { |b|
|
138
|
+
expect { dbl.foo(&b) }.to throw_symbol(:bar)
|
139
|
+
}.to yield_with_args(5)
|
140
|
+
|
141
|
+
expect(block_called).to be_true
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'returns `nil` from all terminal actions to discourage further configuration' do
|
145
|
+
expect(double.stub(:foo).and_return(1)).to be_nil
|
146
|
+
expect(double.stub(:foo).and_raise("boom")).to be_nil
|
147
|
+
expect(double.stub(:foo).and_throw(:foo)).to be_nil
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'allows the terminal action to be overriden' do
|
151
|
+
dbl = double
|
152
|
+
stubbed_double = dbl.stub(:foo)
|
153
|
+
|
154
|
+
stubbed_double.and_return(1)
|
155
|
+
expect(dbl.foo).to eq(1)
|
156
|
+
|
157
|
+
stubbed_double.and_return(3)
|
158
|
+
expect(dbl.foo).to eq(3)
|
159
|
+
|
160
|
+
stubbed_double.and_raise("boom")
|
161
|
+
expect { dbl.foo }.to raise_error("boom")
|
162
|
+
|
163
|
+
stubbed_double.and_throw(:bar)
|
164
|
+
expect { dbl.foo }.to throw_symbol(:bar)
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'allows the inner implementation block to be overriden' do
|
168
|
+
dbl = double
|
169
|
+
stubbed_double = dbl.stub(:foo)
|
170
|
+
|
171
|
+
stubbed_double.with(:arg) { :with_block }
|
172
|
+
expect(dbl.foo(:arg)).to eq(:with_block)
|
173
|
+
|
174
|
+
stubbed_double.at_least(:once) { :at_least_block }
|
175
|
+
expect(dbl.foo(:arg)).to eq(:at_least_block)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'can combine and_call_original, with, and_return' do
|
179
|
+
obj = Struct.new(:value).new('original')
|
180
|
+
obj.stub(:value).and_call_original
|
181
|
+
obj.stub(:value).with(:arg).and_return('value')
|
182
|
+
expect(obj.value).to eq 'original'
|
183
|
+
expect(obj.value(:arg)).to eq 'value'
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'raises an error if `and_call_original` is followed by any other instructions' do
|
187
|
+
dbl = [1, 2, 3]
|
188
|
+
stubbed = dbl.stub(:size)
|
189
|
+
stubbed.and_call_original
|
190
|
+
|
191
|
+
msg_fragment = /cannot be modified further/
|
192
|
+
|
193
|
+
expect { stubbed.and_yield }.to raise_error(msg_fragment)
|
194
|
+
expect { stubbed.and_return(1) }.to raise_error(msg_fragment)
|
195
|
+
expect { stubbed.and_raise("a") }.to raise_error(msg_fragment)
|
196
|
+
expect { stubbed.and_throw(:bar) }.to raise_error(msg_fragment)
|
197
|
+
|
198
|
+
expect { stubbed.once { } }.to raise_error(msg_fragment)
|
199
|
+
|
200
|
+
expect(dbl.size).to eq(3)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Marshal, 'extensions' do
|
4
|
+
# An object that raises when code attempts to dup it.
|
5
|
+
#
|
6
|
+
# Because we manipulate the internals of RSpec::Mocks.space below, we need
|
7
|
+
# an object that simply blows up when #dup is called without using any
|
8
|
+
# partial mocking or stubbing from rspec-mocks itself.
|
9
|
+
class UndupableObject
|
10
|
+
def dup
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#dump' do
|
16
|
+
context 'when rspec-mocks has not been fully initialized' do
|
17
|
+
def without_space
|
18
|
+
stashed_space, RSpec::Mocks.space = RSpec::Mocks.space, nil
|
19
|
+
yield
|
20
|
+
ensure
|
21
|
+
RSpec::Mocks.space = stashed_space
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'does not duplicate the object before serialization' do
|
25
|
+
obj = UndupableObject.new
|
26
|
+
without_space do
|
27
|
+
serialized = Marshal.dump(obj)
|
28
|
+
expect(Marshal.load(serialized)).to be_an(UndupableObject)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when rspec-mocks has been fully initialized' do
|
34
|
+
it 'duplicates objects with stubbed or mocked implementations before serialization' do
|
35
|
+
obj = double(:foo => "bar")
|
36
|
+
|
37
|
+
serialized = Marshal.dump(obj)
|
38
|
+
expect(Marshal.load(serialized)).to be_an(obj.class)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'does not duplicate other objects before serialization' do
|
42
|
+
obj = UndupableObject.new
|
43
|
+
|
44
|
+
serialized = Marshal.dump(obj)
|
45
|
+
expect(Marshal.load(serialized)).to be_an(UndupableObject)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'does not duplicate nil before serialization' do
|
49
|
+
serialized = Marshal.dump(nil)
|
50
|
+
expect(Marshal.load(serialized)).to be_nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -136,6 +136,16 @@ module RSpec
|
|
136
136
|
receiver.foo(:a)
|
137
137
|
}.to raise_error(/expected: 0 times.*received: 1 time/m)
|
138
138
|
end
|
139
|
+
|
140
|
+
it 'prevents confusing double-negative expressions involving `never`' do
|
141
|
+
expect {
|
142
|
+
wrapped.not_to receive(:foo).never
|
143
|
+
}.to raise_error(/trying to negate it again/)
|
144
|
+
|
145
|
+
expect {
|
146
|
+
wrapped.to_not receive(:foo).never
|
147
|
+
}.to raise_error(/trying to negate it again/)
|
148
|
+
end
|
139
149
|
end
|
140
150
|
|
141
151
|
describe "allow(...).to receive" do
|