Rspec: Custom failure message for raise_error test - ruby-on-rails

I'd like to use customised failure messages when testing if an error was raised or not while visiting a page with capybara.
My code:
expect(visit '/page/path').to_not raise_error(ActionView::Template::Error), "my custom error message"
I have also tried to use the lambda syntax (lambda { "my custom error message" }) with no success.
The problem seems to be that the error itself gets precedence for display over my custom error message. I have no interest in the error message - if one is raised, I know what the problem is, and the custom error message gives the solution (it's a long story involving VCR).
Right now I have a sketchy begin/rescue that looks like this:
begin
expect(visit '/page/path').to_not raise_error(ActionView::Template::Error)
rescue ActionView::Template::Error => error
expect(1).to eq(0), "my custom error message"
end
But, as you can see, I have to write a fake, always failing test, expect(1).to eq(0) which seems silly.
Is there any way to force my message to be displayed from the inline custom error message?

First of all you'll need to pass the block you want to execute to the expect method in order for the raise_error matcher to catch the errors raised in the block. Perhaps surprisingly the following fails for this reason:
describe 'Foo' do
it 'raises an error' do
expect(fail "wat").to raise_error(RuntimeError)
end
end
But if we re-write that expectation as follows it passes:
expect{fail "wat"}.to raise_error(RuntimeError)
As Rob mentioned though if you want to specify that an error is not raised you'll need to write your expectation as follows:
expect{fail "wat"}.to_not raise_error
and using that format you can specify a custom error message:
expect{fail "wat"}.to_not raise_error, "custom error message"
The reason why your second block of code seems to work is your visit '/page/path' raises an exception, then your exception handler catches it. It would be equivalent to the following:
begin
visit '/page/path'
rescue ActionView::Template::Error => error
expect(1).to eq(0), "my custom error message"
end

I have the following working locally:
expect { visit '/page/path' }.to_not raise_error, "Nope"
Specifying the class of exception you expect to_not have raised is apparently deprecated, according to RSpec's yelling at the top of the test results:
DEPRECATION: `expect { }.not_to raise_error(SpecificErrorClass)` is
deprecated. Use `expect { }.not_to raise_error` (with no args) instead.

Related

Rspec raise_error test failing even though the method raises an error

My test looks like this:
it 'does return an error when passing a non-subscription or trial membership' do
expect(helper.description_for_subscription(recurring_plan)).to raise_error(RuntimeError)
end
My method returns this:
fail 'Unknown subscription model type!'
Yet Rspec comes back with this failure message:
Failure/Error: expect(helper.description_for_subscription(recurring_plan)).to raise_error(RuntimeError)
RuntimeError:
Unknown subscription model type!
What is going on??
You should wrap the expectation in a block, using {} instead of ():
expect{
helper.description_for_subscription(recurring_plan)
}.to raise_error(RuntimeError)
Check the Expecting Errors section here

Rspec validation error causing a test to fail (but it's my intention to have a validation error)

Rspec
context "has non ascii characters" do
it "will not call get_imdb" do
expect_any_instance_of(Celebrity).not_to receive(:get_imdb)
FactoryGirl.build(:imdb_celebrity, first_name: "Sæthy")
end
end
model:
def celebrity_status
if full_name.ascii_only?
get_imdb ## << -- returns string or nil
end
### If get_imdb returns nil (or isn't called), record is not valid
end
Validation fails if get_imdb returns nil or isn't called. My problem is that I'm trying to test the ascii characters portion of the method, but by doing that - my validation is failing and giving me a "Validation Failed" error in the console when running Rspec tests.
But that's what I want to happen... I want the validation to fail.
How can I solve this?
By itself, a model validation failure should not raise an exception. You may be attempting to save the record, which would case save to fail, and might raise an exception if called via save! or create!.
You can test whether the model is valid by calling valid?
expect(celebrity.valid?).to be false
It is also useful to check the error hash to see that it has the expected contents:
celebrity.valid? # => false
expect(celebrity.errors[:my_error]).to equal "my error message"
If you want to test your code on having exceptions, you should use Rspec's raise_errormatcher:
expect { 3 / 0 }.to raise_exception
More info here: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/raise-error-matcher

How to assert that a Rails method will not throw a NoMethodError

In my Rails application I am doing a unit test where I expect an error when calling a certain method.
test "sending a logout request while not logged should not return an error" do
assert_nothing_raised do
do_a_logout
end
end
Problem is that the test keeps reporting as Error instead as of Failure when do_a_logout errors out.
The error is NoMethodError: NoMethodError: undefined method 'forget' for nil:NilClass
It comes from this method two or three levels below the do_a_logout call
def forget user
user.forget
cookies.delete :user_id
cookies.delete :remember_token
end
Here, the error I am expecting is for user to be nil and fail when calling forget. How can I make the test report a Failure instead of an Error?
You want to use assert_raise with the exception class you're expecting:
assert_raise(NoMethodError) { do_a_logout }
ETA: If you want to test that no error was raised, you can use assert_nothing_raised:
assert_nothing_raised { do_a_logout }
assert_nothing_raised(NoMethodError) { do_a_logout }
However as pointed out on SO and elsewhere, there is no good reason to use this assertion, because its redundant: you never expect an error to be raised, unless you specify otherwise.
Moreover, it gives a less helpful failure than simply running the spec without the assertion. In particular, running with assert_nothing_raised when an error occurs will give you a backtrace only to the assert_nothing_raised call, while running the spec without this assertion will give a backtrace to the actual error that's occurring.
based on #nus comment,
test "sending a logout request while not logged should not return an error" do
do_a_logout
# assert true # optional
end
or
assert_silent { do_a_logout }

Rspec: How to mute internal exception?

I want to check some internal behaviour of method #abc which also raises an error.
def abc
String.class
raise StandardError
end
describe '#abc' do
it 'should call String.class' do
String.should_receive(:class)
end
end
String.class - is just an example of any method call of any class which I want to perform inside this method.
But I got an error:
Failure/Error: #abc
StandardError
How I can mute this exception so this spec would pass?
You cannot "mute" the exception; you can only catch it, either explicitly through a rescue clause or implicitly through an expectation. Note that this is different than substituting a test double for a called method. The exception is still getting raised by the code under test.
If you don't care whether an error is raised, then you can use:
abc rescue nil
to invoke the method. (Note: This will implicitly only catch StandardError)
If you want to using the should or expect syntax, you need to place the code which is going to raise the error within a block, as in the following:
expect {abc}.to raise_error(StandardError)
Combining this with the setting of an expectation that String.class be invoked, you get:
describe '#abc' do
it 'should call String.class' do
expect(String).to receive(:class)
expect {abc}.to raise_error(StandardError)
end
end

Expecting errors in rspec tests

I'm trying to expect an error in an rspec test.
lambda {Participant.create!({:user_id => three.id, :match_id => match.id, :team => 1})}.should raise_error StandardError
For now I'm just using StandardError to make sure it's working.
1) StandardError in 'Participant should never allow more participants than players'.
This game is already full. Cannot add another player.
/home/josiah/Projects/Set-Match/app/models/participant.rb:12:in `do_not_exceed_player_count_in_match'
./spec/models/participant_spec.rb:24:
It clearly throws the error, but my test still fails.
Thoughts?
Since some time, but at least in RSpec 2.5, it is possible to use
expect {raise 'boom'}.to raise_error(RuntimeError, /boom/)
Your syntax looks correct. To debug this, simplify to make sure your spec is coded correctly.
it "should raise an error" do
lambda {raise "boom"}.should raise_error
end
And then add more detail until it breaks.
lambda {raise "boom"}.should raise_error(RuntimeError)
lambda {raise StandardError.new("boom")}.should raise_error(StandardError)
I had to fight with the same symptoms:
def boom
raise "boom"
end
boom.should raise_error
The test above fails because raise_error requires should to be called on a Proc (due to technical reasons, I suppose). Thus, wrapping a method call with a lambda works just fine:
lambda { boom }.should raise_error
Unfortunately, the documentation does not say that explicitly and there is no RSpec Exception that reveals this behavior. There is a two year old ticket for that.

Resources