Skip to content

Commit

Permalink
Merge pull request #718 from rspec/fix-verified-double-expiration
Browse files Browse the repository at this point in the history
Verifying doubles should expire just like normal doubles.
  • Loading branch information
myronmarston committed Jun 19, 2014
2 parents d9bfa15 + 8f50ed4 commit 4500d90
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 63 deletions.
3 changes: 3 additions & 0 deletions Changelog.md
Expand Up @@ -14,6 +14,9 @@ Bug Fixes:

* Fix edge case that triggered “can't add a new key into hash during
iteration" during mock verification. (Sam Phippen, Myron Marston, #711)
* Fix verifying doubles so that when they accidentally leak into another
example, they provide the same clear error message that normal doubles
do. (Myron Marston, #718)

### 3.0.1 / 2014-06-07
[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...v3.0.1)
Expand Down
2 changes: 1 addition & 1 deletion lib/rspec/mocks/space.rb
Expand Up @@ -148,7 +148,7 @@ def self.synchronize
def proxy_not_found_for(id, object)
proxies[id] = case object
when NilClass then ProxyForNil.new(@expectation_ordering)
when TestDouble then object.__build_mock_proxy(@expectation_ordering)
when TestDouble then object.__build_mock_proxy_unless_expired(@expectation_ordering)
when Class
if RSpec::Mocks.configuration.verify_partial_doubles?
VerifyingPartialClassDoubleProxy.new(self, object, @expectation_ordering)
Expand Down
8 changes: 6 additions & 2 deletions lib/rspec/mocks/test_double.rb
Expand Up @@ -53,8 +53,8 @@ def respond_to?(message, incl_private=false)
end

# @private
def __build_mock_proxy(order_group)
__raise_expired_error or TestDoubleProxy.new(self, order_group, @name)
def __build_mock_proxy_unless_expired(order_group)
__raise_expired_error or __build_mock_proxy(order_group)
end

# @private
Expand Down Expand Up @@ -111,6 +111,10 @@ def __mock_proxy
::RSpec::Mocks.space.proxy_for(self)
end

def __build_mock_proxy(order_group)
TestDoubleProxy.new(self, order_group, @name)
end

def __raise_expired_error
return false unless @__expired
ErrorGenerator.new(self, @name).raise_expired_test_double_error
Expand Down
12 changes: 7 additions & 5 deletions lib/rspec/mocks/verifying_double.rb
Expand Up @@ -88,6 +88,13 @@ module ObjectVerifyingDoubleMethods
include TestDouble
include VerifyingDouble

def as_stubbed_const(options = {})
ConstantMutator.stub(@doubled_module.const_to_replace, self, options)
self
end

private

def initialize(doubled_module, *args)
@doubled_module = doubled_module
super(doubled_module.description, *args)
Expand All @@ -99,11 +106,6 @@ def __build_mock_proxy(order_group)
ObjectMethodReference
)
end

def as_stubbed_const(options = {})
ConstantMutator.stub(@doubled_module.const_to_replace, self, options)
self
end
end

# Similar to an InstanceVerifyingDouble, except that it verifies against
Expand Down
2 changes: 1 addition & 1 deletion lib/rspec/mocks/verifying_proxy.rb
Expand Up @@ -54,7 +54,7 @@ def ensure_publicly_implemented(method_name, object)
# isolation.
#
# @private
class VerifyingProxy < Proxy
class VerifyingProxy < TestDoubleProxy
include VerifyingProxyMethods

def initialize(object, order_group, name, doubled_module, method_reference_class)
Expand Down
54 changes: 0 additions & 54 deletions spec/rspec/mocks/double_spec.rb
Expand Up @@ -74,60 +74,6 @@ module Mocks
expect(dbl.send(:some_msg)).to eq("received")
end

context "after it has been torn down" do
let(:dbl) { double }

before do
expect(dbl).to receive(:foo).at_least(:once)
allow(dbl).to receive(:bar)
dbl.foo

RSpec::Mocks.verify
RSpec::Mocks.teardown
RSpec::Mocks.setup
end

it 'disallows previously mocked methods' do
expect { dbl.foo }.to raise_error(ExpiredTestDoubleError)
end

it 'disallows previously stubbed methods' do
expect { dbl.bar }.to raise_error(ExpiredTestDoubleError)
end

it 'disallows stubbing new methods (with receive)' do
expect {
allow(dbl).to receive(:bazz)
}.to raise_error(ExpiredTestDoubleError)
end

it 'disallows stubbing new methods (with receive_messages)' do
expect {
allow(dbl).to receive_messages(:bazz => 3)
}.to raise_error(ExpiredTestDoubleError)
end

it 'disallows stubbing new message chains' do
expect {
allow(dbl).to receive_message_chain(:bazz, :bam, :goo)
}.to raise_error(ExpiredTestDoubleError)
end

it 'disallows mocking new methods' do
expect {
expect(dbl).to receive(:bazz)
}.to raise_error(ExpiredTestDoubleError)
end

it 'disallows being turned into a null object' do
expect { dbl.as_null_object }.to raise_error(ExpiredTestDoubleError)
end

it 'disallows being checked for nullness' do
expect { dbl.null_object? }.to raise_error(ExpiredTestDoubleError)
end
end

it "reports line number of expectation of unreceived message" do
expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3)
expect {
Expand Down
90 changes: 90 additions & 0 deletions spec/rspec/mocks/expiration_spec.rb
@@ -0,0 +1,90 @@
module RSpec
module Mocks
RSpec.describe "After a test double has been torn down" do
shared_examples_for "expiration" do
before do
expect(dbl).to receive(:foo).at_least(:once)
allow(dbl).to receive(:bar)
dbl.foo

RSpec::Mocks.verify
RSpec::Mocks.teardown
RSpec::Mocks.setup
end

it 'disallows previously mocked methods' do
expect { dbl.foo }.to raise_error(ExpiredTestDoubleError)
end

it 'disallows previously stubbed methods' do
expect { dbl.bar }.to raise_error(ExpiredTestDoubleError)
end

it 'disallows stubbing new methods (with receive)' do
expect {
allow(dbl).to receive(:bazz)
}.to raise_error(ExpiredTestDoubleError)
end

it 'disallows stubbing new methods (with receive_messages)' do
expect {
allow(dbl).to receive_messages(:bazz => 3)
}.to raise_error(ExpiredTestDoubleError)
end

it 'disallows stubbing new message chains' do
expect {
allow(dbl).to receive_message_chain(:bazz, :bam, :goo)
}.to raise_error(ExpiredTestDoubleError)
end

it 'disallows mocking new methods' do
expect {
expect(dbl).to receive(:bazz)
}.to raise_error(ExpiredTestDoubleError)
end

it 'disallows being turned into a null object' do
expect { dbl.as_null_object }.to raise_error(ExpiredTestDoubleError)
end

it 'disallows being checked for nullness' do
expect { dbl.null_object? }.to raise_error(ExpiredTestDoubleError)
end

end

context "for a plain double" do
let(:dbl) { double }
include_examples "expiration"
end

class ExpiredInstanceInterface
def foo; end
def bar; end
def bazz; end
end

class ExpiredClassInterface
def self.foo; end
def self.bar; end
def self.bazz; end
end

context "for an instance_double" do
let(:dbl) { instance_double(ExpiredInstanceInterface) }
include_examples "expiration"
end

context "for a class_double" do
let(:dbl) { class_double(ExpiredClassInterface) }
include_examples "expiration"
end

context "for an object_double" do
let(:dbl) { object_double(ExpiredInstanceInterface.new) }
include_examples "expiration"
end
end
end
end

0 comments on commit 4500d90

Please sign in to comment.