[rspec-users] and_raise with a message

David Chelimsky dchelimsky at gmail.com
Wed Aug 22 11:25:27 UTC 2012


On Wed, Aug 22, 2012 at 12:56 AM, Bas Vodde <basv at odd-e.com> wrote:
>
> Hiya all,
>
> I was trying to get and_raise to raise an exception filled with a message and I was struggling with the API for a while (not on the latest RSpec, but assume it didn't change).
>
> Based on that, I have a suggestion for improvement. My first attempt was to mirror how I use raise, so I tried:
>
>     subject.should_receive(:do_system).and_raise(Osaka::SystemCommandFailed, "execution error: System Events got an error: Access for assistive devices is disabled. (-25211)")
>
> This didn't work as the and_raise only has one parameter. Eventually I figured out this works:
>
>     subject.should_receive(:do_system).and_raise(Osaka::SystemCommandFailed.new ("execution error: System Events got an error: Access for assistive devices is disabled. (-25211)"))
>
> And it does what I want it to do. Still… I would have prefered the first one to work too :) Implementing that ought to be reasonably trivial, correct?
> (didn't implement it myself yet this time).
>
> Comments? Or is there anyone way of doing this?
>
> Thanks!
>
> Bas

Documentation here:
http://rubydoc.info/gems/rspec-mocks/RSpec/Mocks/MessageExpectation#and_raise-instance_method.

That has been the API for something like 5 years so I'm not sure I
want to change it. It is used to prepare an exception to raise, not
compare with one that was already raised, so we'd have to support n
args, e.g.

and_raise(AnException, with_one_arg)
and_raise(AnException, with_one_arg, and_another_arg)

etc. I think this would be friendly, but it might also be confusing
because we'd effectively have 2 completely different APIs for the same
method. Also, I'm not sure that either of those examples are much
better than:

and_raise(AnException.new with_one_arg)
and_raise(AnException.new with_one_arg, and_another_arg)

I'm open to the idea though for one reason: the rspec-expectations
raise_exception method can accept 1 or two args, so I can imagine
something like:

describe Foo do
  it "raises when x happens" do
    foo = Foo.new(Bar.new)
    expect { Foo.do_something_bad }.to raise_exception(FooException,
"with this message")
  end

  it "does x when its bar raises" do
    bar = double
    foo = Foo.new(bar)
    bar.should_receive(:msg).and_raise(BarException, "with this message")
  end
end

Your suggestion makes these two APIs align better if the exception
initializer accepts one argument and raise_exception gets a String.
But raise_exception can also take a regex, and exception initializers
can take more than one arg. Consider: InsufficientFunds.new(50, :USD).
The way things are today, you might see these two near each other
(hopefully not in the same example):

  expect { transfer.execute }.to raise_exception(InsufficientFunds, /50 USD/)
  source_account.should_receive(:withdraw).and_raise(InsufficientFunds.new
50, :USD)

With what you propose it would be this:

  expect { transfer.execute }.to raise_exception(InsufficientFunds, /50 USD/)
  source_account.should_receive(:withdraw).and_raise(InsufficientFunds,
50, :USD)

I think the way it is now tells a better story about what's really
going on (i.e. that you're supplying an initialized object to
and_raise) than the proposed change. But that's one opinion.

Any others out there?


More information about the rspec-users mailing list