Skip to content
This repository has been archived by the owner on Oct 27, 2021. It is now read-only.

Commit

Permalink
unstub! a message which has been stub!'ed
Browse files Browse the repository at this point in the history
[#853 state:resolved milestone:'Next Release']
  • Loading branch information
smtlaissezfaire authored and dchelimsky committed Jul 15, 2009
1 parent aa1d064 commit 281dc54
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 8 deletions.
6 changes: 5 additions & 1 deletion lib/spec/mocks/message_expectation.rb
Expand Up @@ -181,9 +181,13 @@ def failed_fast?
end

class MessageExpectation < BaseExpectation

def matches_name?(sym)
@sym == sym
end

def matches_name_but_not_args(sym, args)
@sym == sym and not @args_expectation.args_match?(args)
matches_name?(sym) and not @args_expectation.args_match?(args)
end

def verify_messages_received
Expand Down
6 changes: 6 additions & 0 deletions lib/spec/mocks/methods.rb
Expand Up @@ -19,6 +19,12 @@ def stub!(sym_or_hash, opts={}, &block)

alias_method :stub, :stub!

def unstub!(message)
__mock_proxy.remove_stub(message)
end

alias_method :unstub, :unstub!

def stub_chain(*methods)
if methods.length > 1
if matching_stub = __mock_proxy.find_matching_method_stub(methods[0])
Expand Down
29 changes: 22 additions & 7 deletions lib/spec/mocks/proxy.rb
Expand Up @@ -61,6 +61,17 @@ def add_stub(expected_from, sym, opts={}, &implementation)
@stubs.unshift MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, nil, :any, opts, &implementation)
@stubs.first
end

def remove_stub(message)
message = message.to_sym

if stub_to_remove = @stubs.detect { |s| s.matches_name?(message) }
reset_proxied_method(message)
@stubs.delete(stub_to_remove)
else
raise MockExpectationError, "The method `#{message}` was not stubbed or was already unstubbed"
end
end

def verify #:nodoc:
verify_expectations
Expand Down Expand Up @@ -197,13 +208,17 @@ def verify_expectations

def reset_proxied_methods
@proxied_methods.each do |sym|
munged_sym = munge(sym)
target_metaclass.instance_eval do
remove_method sym
if method_defined?(munged_sym)
alias_method sym, munged_sym
remove_method munged_sym
end
reset_proxied_method(sym)
end
end

def reset_proxied_method(sym)
munged_sym = munge(sym)
target_metaclass.instance_eval do
remove_method sym
if method_defined?(munged_sym)
alias_method sym, munged_sym
remove_method munged_sym
end
end
end
Expand Down
127 changes: 127 additions & 0 deletions spec/spec/mocks/unstub_spec.rb
@@ -0,0 +1,127 @@
require File.dirname(__FILE__) + '/../../spec_helper'

module Spec
module Mocks
describe Mock do
context "unstubbing a mock object with a stub" do
it "should remove the stub" do
a_mock = mock 'an object', :foo => :bar

a_mock.unstub! :foo
a_mock.should_not respond_to(:foo)
end
end

context "unstubbing a real object with a stub" do
before do
@obj = Object.new
end

it "should raise a NoMethodError if the message is called after unstubbing" do
@obj.stub!(:foo).and_return :bar
@obj.unstub!(:foo)

lambda {
@obj.foo
}.should raise_error(NoMethodError)
end

it "should only clear the stub specified" do
@obj.stub!(:foo).and_return :bar
@obj.stub!(:other).and_return :baz

@obj.unstub!(:foo)

@obj.other.should == :baz
end

it "should no longer respond_to? the method" do
@obj.stub!(:foo).and_return :bar
@obj.unstub!(:foo)

@obj.should_not respond_to(:foo)
end

it "should unstub using a string (should convert the string to a symbol)" do
@obj.stub!(:foo)

@obj.unstub!("foo")

@obj.should_not respond_to(:foo)
end

it "should restore a previous method definition" do
def @obj.foo
:a_result
end

@obj.stub!(:foo).and_return :stubbed_result
@obj.unstub!(:foo)

@obj.foo.should == :a_result
end

it "should have unstub as an alias of unstub!" do
@obj.stub!(:foo).and_return :bar

@obj.unstub(:foo)

lambda {
@obj.foo
}.should raise_error(NoMethodError)
end

it "should raise a MockExpectationError if it is not stubbed" do
lambda {
@obj.unstub!(:foo)
}.should raise_error(MockExpectationError, "The method `foo` was not stubbed or was already unstubbed")
end

it "should raise a MockExpectationError if it was already unstubbed" do
@obj.stub!(:foo)
@obj.unstub!(:foo)

lambda {
@obj.unstub!(:foo)
}.should raise_error(MockExpectationError, "The method `foo` was not stubbed or was already unstubbed")
end

it "should use the correct message name in the error" do
@obj.stub!(:bar)
@obj.unstub!(:bar)

lambda {
@obj.unstub!(:bar)
}.should raise_error(MockExpectationError, "The method `bar` was not stubbed or was already unstubbed")
end

it "should raise a MockExpectationError if the method is defined, but not stubbed" do
def @obj.meth; end

lambda {
@obj.unstub!(:meth)
}.should raise_error(MockExpectationError)
end

it "should be able to restub a after unstubbing" do
@obj.stub!(:foo).and_return :bar

@obj.unstub!(:foo)

@obj.stub!(:foo).and_return :baz

@obj.foo.should == :baz
end

it "should remove only the first stub if multiple stubs have been defined" do
@obj.stub!(:foo).and_return :first
@obj.stub!(:foo).and_return :second

@obj.unstub!(:foo)

@obj.foo.should == :first
end
end
end
end
end

0 comments on commit 281dc54

Please sign in to comment.