Rails ActiveSupport: How to assert that an error is raised? - ruby-on-rails

I am wanting to test a function on one of my models that throws specific errors. The function looks something like this:
def merge(release_to_delete)
raise "Can't merge a release with itself!" if( self.id == release_to_delete.id )
raise "Can only merge releases by the same artist" if( self.artist != release_to_delete.artist )
#actual merge code here
end
Now I want to do an assert that when I call this function with a parameter that causes each of those exceptions, that the exceptions actually get thrown. I was looking at ActiveSupport documentation, but I wasn't finding anything promising. Any ideas?

So unit testing isn't really in activesupport. Ruby comes with a typical xunit framework in the standard libs (Test::Unit in ruby 1.8.x, MiniTest in ruby 1.9), and the stuff in activesupport just adds some stuff to it.
If you are using Test::Unit/MiniTest
assert_raise(Exception) { whatever.merge }
if you are using rspec (unfortunately poorly documented, but way more popular)
lambda { whatever.merge }.should raise_error
If you want to check the raised Exception:
exception = assert_raises(Exception) { whatever.merge }
assert_equal( "message", exception.message )

To ensure that no exception is raised (or is successfully handled) do inside your test case:
assert_nothing_raised RuntimeError do
whatever.merge
end
To check that error is raised do inside your test case:
assert_raise RuntimeError do
whatever.merge
end
Just a heads up, whatever.merge is the code that raises the error (or doesn't, depending on the assertion type).

Related

Rspec: Custom failure message for raise_error test

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.

How to rescue the error exception raised by the `constantize` method?

I am using Ruby on Rails 3.2.2 and I would like to properly rescue the following process flow by raising a "custom" error message:
def rescue_method
# sample_string.class
# => String
# sample_string.inspect
# => "ARubyConstantThatDoesNotExist"
begin
build_constant(sample_string)
rescue
raise("My custom error message: #{build_constant(sample_string)} doesn't exist.")
end
end
def build_constant(sample_string)
"AModule::#{sample_string}".constantize
end
Note: I feel "forced" to use the constantize method also in the raised "custom" message in order to DRY code...
When the rescue_method is executed it seems that the raise("My custom error message") code is never executed and I get the following error:
uninitialized constant AModule::ARubyConstantThatDoesNotExist
How to properly display the raised "custom" message (since a further error exception is raised in the subsequent raised "custom" message)? What do you advice about?
The problem is that your build_constant method is doing two things:
Building the class name.
Turning the name into a class using constantize.
One of those things wants to use the other when an exception is raised. A simple solution is to pull those separate tasks apart:
def build_class_name(sample_string)
"AModule::#{sample_string}"
end
def rescue_method
name = build_class_name(...)
name.constantize
rescue NameError
raise("My custom error message: #{name} doesn't exist.")
end
You should also be more specific about the exception you're looking for so I added that for free.
If you don't want to rely on catching any exception, you could use safe_constantize (https://apidock.com/rails/ActiveSupport/Inflector/safe_constantize).
Same purpose as constantize but will return nil instead, whenever a Module does not exist.
'UnknownModule::Foo::Bar'.safe_constantize # => nil
begin
"AModule::#{sample_string}".constantize
rescue SyntaxError, NameError => err
raise("My custom error message")
end

Capture the outcome of an assert statement?

I am writing a test helper and it has this method:
def todo msg = ''
assert false, '#TODO: ' + msg
end
Basically I want a quick method to fail a test. Next, I would like to test this method (because it will be encapsulated in a shippable helper). So I would like to write something like this:
test 'todo' do
result = todo
expected = '#TODO: '
assert_equal expected, result
end
but it just fails when assert false gets called. So, how would I test this method?
This helper is supposed to work with Test::Unit, is that right? I believe that assert false throws an exception which is caught by Test::Unit. You could just wrap the call to todo in a begin...rescue block and catch that exception.
The test/unit which comes in the Ruby standard libraries is actually Minitest::Unit -- when an assertion fails, it raises a Minitest::Assertion exception. You could rescue that.
For the "real" Test::Unit, it raises a Test::Unit::AssertionFailedError when an assertion fails.

RSpec for should_receive and should_not_receive both passed for Exception

I had a really weird rspec case scenario. I tried to test if my function handles exception correctly. And the following is my code:
in User.rb:
def welcome_user
begin
send_welcome_mail(self)
rescue Exception => exception
ErrorMessage.add(exception, user_id: self.id)
end
end
end
in user_spec.rb
it "adds to error message if an exception is thrown" do
mock_user = User.new
mock_user.stub(:send_welcome_mail).and_raise(Exception)
ErrorMessage.should_receive(:add)
mock_user.welcome_user
end
The test passed, but when I change ErrorMessage.should_receive(:add) to ErrorMessage.should_not_receive(:add), It also passed, any insights?
Since rspec 2.11 exposed one of my tests to exhibit such "abnormality", I decided to raise the issue on github. You may following the discussion at https://github.com/rspec/rspec-mocks/issues/164
Summary: any_instance.should_not_receive is undefined, avoid
What you can try to use instead is .should_receive in combination with .never:
ErrorMessage.should_receive(:add).never

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