How can I check if my subject raises an exception?
Asked Answered
L

2

7

I'm currently creating an object in subject and need to test if this raises an exception. The following code illustrates what I'm trying to achieve:

describe MyClass do
  describe '#initialize' do
    subject { MyClass.new }

    it { is_expected.not_to raise_error(Some::Error) }
  end
end

I have a feeling I'm going about this the wrong way. What is the preferred way to set the subject to a new object, without creating the object twice?


Update

My problem was two-fold. Firstly, this syntax does not work:

it { is_expected.not_to raise_error }

Using expect inside an it block does, however (as pointed out by Jimmy Cuadra):

it 'does not raise an error' do
  expect { subject }.not_to raise_error
end

I am not well enough acquainted with RSpec to tell you why this is.

Secondly, since RSpec 3.0.0.beta1, it is longer possible to use raise_error with a specific error class. The following, therefore, is invalid:

expect { subject }.to raise_error(Some::Error)

For more information, see

Livvi answered 27/7, 2014 at 18:2 Comment(2)
You have an implicit subject, so you don't need subject { MyClass.new }.Psoas
Handy to know, @ArupRakshit! Although the constructor in my simplified example did not take arguments, though, the one in my actual class does.Livvi
P
12

If I'm understanding correctly, you're trying to test if instantiating a class causes an exception. You would just do this:

describe MyClass do
  it "doesn't raise an exception when instantiated" do
    expect { subject }.not_to raise_error
  end
end 
Pothole answered 27/7, 2014 at 18:10 Comment(2)
I'm accepting this because it is indeed the answer to my question. I've updated the question in case others have similar issues, as there was another issue.Livvi
The key here is you need to put the subject inside of an expect block. You can't use it { is_expected.not_to raise_error } and you can use expect { subject.not_to raise_error }. You need to use expect { subject }.not_to raise_error. Cheers for the answer here.Rafat
C
7

The right way to do that through is_expected syntax is to wrap your subject value by a Proc, like the following example:

describe MyClass do
  describe '#initialize' do
    subject { -> { MyClass.new } }
    it      { is_expected.not_to raise_error(Some::Error) }
  end
end

This way is more accurate, because sometimes your use case is to expect that specific kinds of exceptions should not be thrown (while others are allowed to be thrown). This approach will cover such use cases.

Childers answered 17/9, 2015 at 21:59 Comment(9)
Yes, it does. Just make sure you RSpec version supports is_expected method.Childers
Looks like it just checks if a Proc object raises an exception 😀Proportioned
@OlegAfanasyev Yes. Basically raise_error will internally invoke .call on the Proc stored/returned by the subject.Childers
@leandroico, I did not find any .call invocations in the source code nor that it would make any sense to make a special case for subjects evaluating to procs because a subject is already wrapped in a expect {} block and is not invoked until test runtime.Proportioned
@OlegAfanasyev I believe this Proc-based behavior comes from raise_error and not something else.Childers
@leandroico, I found it here, thanks! It's worth mentioning that your solution works only for expect() without block, i.e. expect {} won't call the initial proc.Proportioned
@OlegAfanasyev The thing is that the whole original comment of mine and the subsequent minor comments that follow are actually around the is_expected method.Childers
This is not "the right way to do it". This messes up non-raise assertions. The right way to do it is to put the subject into its own block, like expect { subject }.not_to raise_error. See @Pothole Cuadra's answer: https://mcmap.net/q/1409271/-how-can-i-check-if-my-subject-raises-an-exceptionRafat
This is officially deprecated on rspec-expectations v3.11.0 (2022-02-09). See github.com/rspec/rspec-expectations/blob/main/… and github.com/rspec/rspec-expectations/pull/1139Peshitta

© 2022 - 2024 — McMap. All rights reserved.