megas
megas

Reputation: 21791

How to extend rspec mock expectation?

I want to extend and_return method to raise exception if the argument is kind of exception. For example

obj.stub(:meth).and_return(SomeException,"message")

This construction should raise an exception on a first call and just return the string on a second call.

How to extend rspec by this method, is there a guideline for such kind of task?

UPDATE:

General notation of this function could be:

and_return_or_raise(list of arguments or/and exceptions)

Upvotes: 0

Views: 628

Answers (3)

zetetic
zetetic

Reputation: 47548

You don't need to extend anything--just use RSpec's built-in error matcher and receive counts:

class Foobar
  def foo
    bar
  end
end

it "raises the first time, then returns a string" do
  obj = Foobar.new
  obj.should_receive(:bar).once.and_raise(StandardError)
  obj.should_receive(:bar).once.and_return("message")
  expect { obj.foo }.to raise_error(StandardError)
  obj.foo.should == "message"
end

Upvotes: 1

Andrew Haines
Andrew Haines

Reputation: 6644

So, the actual business of returning the multiple values is in this method in the RSpec::Mocks::MessageExpectation class:

def call_implementation_consecutive(*args, &block)
  @value ||= call_implementation(*args, &block)
  @value[[@actual_received_count, @value.size-1].min]
end

Basically, call_implementation returns the list of expected return values that you passed to and_return, and this method picks out the one corresponding to the current invocation (returning the last value if we call the method more times than there are values in the list).

So, to do what you're after, you could monkey-patch this method as follows:

class RSpec::Mocks::MessageExpectation
  alias_method :old_call_implementation_consecutive, :call_implementation_consecutive

  def call_implementation_consecutive(*args, &block)
    old_call_implementation_consecutive(*args, &block).tap do |value|
      raise value if value.is_a?(Class) && value < Exception
    end
  end
end

Upvotes: 1

EdvardM
EdvardM

Reputation: 3072

How about

stuff = [SomeException, "message"]
obj.stub(:meth).and_return do
  i = stuff.shift
  if i.respond_to?(:downcase)
    i
  else
    raise i
  end
end    

surely not the prettiest way, but should do the job in your particular case.

Upvotes: 1

Related Questions