rspec: raise_error usage to match error message - ruby

I raised an error using raise(ConfigurationError.new(msg))
I tried to test this with rspec:
expect {
Base.configuration.username
}.to raise_error(ConfigurationError, message)
But this doesn't work. How can I test this? The goal is to match message.

You can match error message with regex:
it { expect{ Foo.bar }.to raise_error(NoMethodError, /private/) }
This will check if NoMethodError raised with private method message not undefined method.
Will be useful because NoMethodError.new didn't pass tests even with same error message.

Make sure you are using rspec > 2.14.0 and take a look at this commit:
https://github.com/rspec/rspec-expectations/commit/7f02b503d5ae48d1141b6465acd0a7a4e1bb84dd
it "passes if an error instance is expected" do
s = StandardError.new
expect {raise s}.to raise_error(s)
end

An alternative solution is to use a block where you assert on various properties of |error|. Using the names in the original question:
expect { Base.configuration.username }
.to(raise_error do |error|
expect(error).to be_a(ConfigurationError)
expect(error.message).to eq 'My configuration message'
end)

Related

RSpec 3: expect object.do_something to NOT raise a particular kind of error

I would like to test whether a particular method does NOT raise an error of class AError. It can raise BError, ArgumentError, almost any other kind of error, or no error, just not AError. Is there any non-deprecated (as of RSpec 3) way to do this?
I tried
expect { object.do_something }.not_to raise_error(AError)
but I get
ArgumentError:
`expect { }.not_to raise_error(SpecificErrorClass)` is not valid,
use `expect { }.not_to raise_error` (with no args) instead
The problem with the argument-less approach is that the test will fail on ANY kind of error, when it should pass on anything except for an AError.
This documentation doesn't seem to help: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/raise-error-matcher
It seems that the old versions of RSpec have a way to handle this type of situation, and I don't understand what happened to RSpec in the new version.
So I'm confused. Thanks.
This specific syntax solved the problem:
it 'do_something does not raise AError' do
begin
expect { object.do_something }.not_to raise_error
rescue RSpec::Expectations::ExpectationNotMetError => e
expect(e.message).not_to include 'AError'
end
end
You can read discussion about why this was removed here https://github.com/rspec/rspec-expectations/issues/231
If you really must to test this case, you can do this differently:
begin
object.do_something
rescue StandardError => e
# expect e to not be AError
end

How to raise standard error from a rspec test?

So I have an rspec test that I am trying to get my program to validate against:
code snippet from rspec test:
context '#validate(test_tool)' do
it { expect(test_tool.validate).to raise_error StandardError }
code snippet from test_tool (test_tool is the program I wrote to pass the rspec test) Inside test_tool there are multiple methods, but I just pulled out the one method that the rspec test is looking for:
def self.validate
raise StandardError
end
I have googled everywhere. No matter how I attempt to raise this error. I keep getting an error. Please see snippet of console output:
example at ./spec/training_site/spec_test.rb:56 (FAILED - 1)
Failures:
1) MyClass #validate(test_tool)
Failure/Error: it { expect(MyClass.validate).to raise_error StandardError }
StandardError:
StandardError
Line 56 is just the 2nd line from the 1st code snippet.
Basically, I'm looking for troubleshooting and or example on how I should accomplish this.
This is correct spec. You should use {} (block brackets) since you are waiting for it to raise error. Simple brackets that you have use are for values checking.
context '#validate(test_tool)' do
it { expect{test_tool.validate}.to raise_error StandardError }

How can I check if my subject raises an exception?

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
Rspec 3.0.0.beta1 changelog
Consider deprecating `expect { }.not_to raise_error(SpecificErrorClass)` #231
Remove expect {}.not_to raise_error(SomeSpecificClass) #294
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
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.

RSpec, implicit subject, and exceptions

Is there a way to properly test exception raising with implicit subjects in rspec?
For example, this fails:
describe 'test' do
subject {raise 'an exception'}
it {should raise_exception}
end
But this passes:
describe 'test' do
it "should raise an exception" do
lambda{raise 'an exception'}.should raise_exception
end
end
Why is this?
subject accepts a block which returns the subject of the remainder.
What you want is this:
describe 'test' do
subject { lambda { raise 'an exception' } }
it { should raise_exception }
end
Edit: clarification from comment
This:
describe 'test' do
subject { foo }
it { should blah_blah_blah }
end
is more or less equivalent to
(foo).should blah_blah_blah
Now, consider: without the lambda, this becomes:
(raise 'an exception').should raise_exception
See here that the exception is raised when the subject is evaluated (before should is called at all). Whereas with the lambda, it becomes:
lambda { raise 'an exception' }.should raise_exception
Here, the subject is the lambda, which is evaluated only when the should call is evaluated (in a context where the exception will be caught).
While the "subject" is evaluated anew each time, it still has to evaluate to the thing you want to call should on.
The other answer explains the solution pretty well. I just wanted to mention that RSpec has a special helper called expect. It's just a little easier to read:
# instead of saying:
lambda { raise 'exception' }.should raise_exception
# you can say:
expect { raise 'exception' }.to raise_error
# a few more examples:
expect { ... }.to raise_error
expect { ... }.to raise_error(ErrorClass)
expect { ... }.to raise_error("message")
expect { ... }.to raise_error(ErrorClass, "message")
More information can be found in the RSpec documentation on the built-in matchers.

rspec expect throws "undefined method"

I am a complete Ruby newby and am playing around with rspec
I am testing a class (Account) that has this line:
attr_reader :balance
When I try to test it with this method:
it "should deposit twice" do
#acc.deposit(75)
expect {
#acc.deposit(50)
}.to change(Account.balance).to(125)
end
I get this error:
NoMethodError in 'Account should deposit twice'
undefined method `balance' for Account:Class
I don't understand why I get the error since the attribute 'balance' exists, however I can see that it is not a method, but shouldn't rspec be able to find it anyway?
Update:
As Jason noted I should be #acc.balance, since this is what I am asserting. But I get 'nil is not a symbol' when doing this.
It should be #acc.balance
it "should deposit twice" do
#acc = Account.new
#acc.deposit(75)
#acc.balance.should == 75
expect {
#acc.deposit(50)
}.to change(#acc, :balance).to(125)
end
i think it should be
expect {#acc.deposit(50)}.to change(#acc.balance}.to(125)
It should be:
it "should deposit twice" do
#acc.deposit(75)
expect {
#acc.deposit(50)
}.to change { #acc.balance }.to(125)
end
Please note that you need use curly braces { ... } instead of parentheses ( ... ) around #acc.balance. Otherwise #acc.balance is evaluated before it is passed to change method which expects either symbol or block.

Resources