David Tuite
David Tuite

Reputation: 22653

Can I stub "raise" in Ruby?

I have a class called RemoteError with a self.fatal method on it. This methods job is basically to catch an exception, send the details of the exception to a server and then propagate the exception in order to kill the program.

class RemoteError
  def initialize(label, error)
    @label = label
    @error = error
  end

  def self.fatal(label, error)
    object = new(label, error)
    object.send
    raise error
  end

  def send
    # send the error to the server
  end
end

I'm trying to write tests for the RemoteError.fatal method. This is difficult because of the call to raise within the method. Every time I run my tests, raise obviously raises an exception and I can't test that send was called.

  describe "fatal" do
    it "should send a remote error" do
      error = stub
      RemoteError.stub(:new) { error }
      error.should_receive(:send)

      RemoteError.fatal(stub, stub)
    end
  end

Is there a way that I can stub or somehow circumvent raise for this specific test?

Upvotes: 2

Views: 722

Answers (2)

jefflunt
jefflunt

Reputation: 33954

You could wrap the method that raises the error in a lambda...

it "should send a remote error" do
  ...

  lambda { RemoteError.fatal(stub, stub) }.should raise_error(error)
end    

This essentially allows the method to be called, and you get the return value or raised error from it, which you then assert against with .should raise_error(error). This also makes is so that if no error is raised from that call, the test will fail normally.

To say it another way, you don't need, nor want, to stub raise. Simply wrap it in a lambda and your code will continue to execute, you should be able to make sure that your message is sent, and your test won't exit/crash.

Upvotes: 9

Aliaksei Kliuchnikau
Aliaksei Kliuchnikau

Reputation: 13719

In your tests you are testing the method that raises an error and this is an expected result of method execution. You should write expectation for exception with this syntax:

lambda{ RemoteError.fatal(stub, stub) }.should raise_error

In this case your spec will fail if exception won't be raised and also will fail if all other expectations (like should_receive(:sen)) won't be met.

Upvotes: 3

Related Questions