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.
Files changed (35) hide show
  1. data/Changelog.md +25 -0
  2. data/features/message_expectations/allow_any_instance_of.feature +26 -0
  3. data/features/message_expectations/expect_any_instance_of.feature +27 -0
  4. data/features/message_expectations/expect_message_using_expect.feature +4 -0
  5. data/features/method_stubs/README.md +28 -2
  6. data/features/method_stubs/any_instance.feature +5 -1
  7. data/features/method_stubs/as_null_object.feature +6 -1
  8. data/features/method_stubs/simple_return_value_with_allow.feature +44 -0
  9. data/features/method_stubs/{simple_return_value.feature → simple_return_value_with_stub.feature} +0 -0
  10. data/features/method_stubs/stub_implementation.feature +23 -1
  11. data/lib/rspec/mocks.rb +35 -0
  12. data/lib/rspec/mocks/any_instance/chain.rb +27 -18
  13. data/lib/rspec/mocks/any_instance/expectation_chain.rb +1 -28
  14. data/lib/rspec/mocks/any_instance/recorder.rb +1 -2
  15. data/lib/rspec/mocks/any_instance/stub_chain.rb +2 -1
  16. data/lib/rspec/mocks/error_generator.rb +7 -0
  17. data/lib/rspec/mocks/extensions/marshal.rb +7 -7
  18. data/lib/rspec/mocks/matchers/receive.rb +5 -1
  19. data/lib/rspec/mocks/message_expectation.rb +142 -63
  20. data/lib/rspec/mocks/method_double.rb +0 -9
  21. data/lib/rspec/mocks/proxy.rb +0 -5
  22. data/lib/rspec/mocks/proxy_for_nil.rb +2 -1
  23. data/lib/rspec/mocks/syntax.rb +15 -5
  24. data/lib/rspec/mocks/test_double.rb +2 -2
  25. data/lib/rspec/mocks/version.rb +1 -1
  26. data/spec/rspec/mocks/any_instance_spec.rb +26 -0
  27. data/spec/rspec/mocks/block_return_value_spec.rb +18 -0
  28. data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +205 -0
  29. data/spec/rspec/mocks/extensions/marshal_spec.rb +54 -0
  30. data/spec/rspec/mocks/matchers/receive_spec.rb +10 -0
  31. data/spec/rspec/mocks/mock_spec.rb +31 -3
  32. data/spec/rspec/mocks/partial_mock_spec.rb +3 -3
  33. data/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +81 -0
  34. data/spec/rspec/mocks/test_double_spec.rb +16 -6
  35. metadata +24 -9
@@ -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
- Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{caller[2]}. Use allow_message_expectations_on_nil to disable warnings.")
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
@@ -12,20 +12,21 @@ module RSpec
12
12
 
13
13
  syntax_host.class_eval do
14
14
  def should_receive(message, opts={}, &block)
15
- proxy = ::RSpec::Mocks.proxy_for(self)
16
- proxy.add_message_expectation(opts[:expected_from] || caller(1)[0], message.to_sym, opts, &block)
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
- proxy = ::RSpec::Mocks.proxy_for(self)
21
- proxy.add_negative_message_expectation(caller(1)[0], message.to_sym, &block)
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
- ::RSpec::Mocks.proxy_for(self).add_stub(caller(1)[0], message_or_hash.to_sym, opts, &block)
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
- stub(message).and_return(response)
113
+ Mocks.allow_message(self, message).and_return(response)
114
114
  end
115
115
  end
116
116
 
@@ -1,7 +1,7 @@
1
1
  module RSpec
2
2
  module Mocks
3
3
  module Version
4
- STRING = '2.14.0.rc1'
4
+ STRING = '2.14.0'
5
5
  end
6
6
  end
7
7
  end
@@ -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