Can I assert inside a begin rescue end block? - ruby

I have something that I am testing that I will know its working if it fails. Is there a better way to code this in ruby with test-unit than what I have in my example below?
begin
x = Method.shouldFail
assert_true(false)
rescue Test::Unit::AssertionFailedError
assert_true(false) #stop test, this is a failure
rescue => e
assert_equal(400, e.code)
end
This seems very clunky is there a better way to write this? I would expect that Method.shouldFail would always fail but it might not. And I would assume that in the last rescue block e.code should always be 400 but it could be something else.

You can use assert_raise to test that particular exceptions are being thrown.

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 do I test yield_control multiple times to a block that raises an error?

I'd like to use the yield_control matcher in RSpec like so
expect {|b| some_method(&b) }.to yield_control.exactly(5).times
However, I'd like the block to raise an error. Is there an RSpec construct that allows you to do this?
Basically, I'm looking for something like an RSpec mock object that allows you to raise an error whenever the block is yielded to and count the number of times it was yielded to.
I understand there are other ways of doing it by defining my own block that keeps track of how many times it has been called, but I was hoping there is a built-in that does something like this.
Example:
def some_method(&block)
attempt = 0
begin
attempt += 1
yield
rescue => e
retry if attempt < 3
end
end
I'd like to test that control is yielded a certain number of times when the block raises an error. I understand that this is one way to do it:
number_of_times = 0
some_method do
number_of_times += 1
raise StandardError
end
expect(number_of_times).to eq(3)
But I was hoping I could use the expect {..}.to yield_control syntax.
Are you expecting the block to raise an error? There is an Rspec matcher for that: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/raise-error-matcher

Ruby: Assert_raise alternative?

I'm working out Ruby Kons and I stopped in about_Hashes. It took me a bit to understand what asserts are and how they work and I think I get it but suddenly assert_raise appears! I'm confused and now even google can explain me clearly how does it works. So basically there is my question:
Does this piece of code:
hash = { :one => "uno" }
assert_raise(KeyError) do
hash.fetch(:doesnt_exist)
end
Equal this piece of code:
hash = {:one => "uno"}
begin
hash.fetch(:doesnt_exist)
rescue Exception => exp
exp.message
end
Am I correct?
You're close - assert_raise in your case would look more like this:
hash = {:one => "uno"}
begin
hash.fetch(:doesnt_exist)
rescue KeyError
# Test passes if this happens
rescue Exception
# Test fails if any other exception is raised, must be KeyError
end
# Test fails if no exception is raised
The only difference is that it makes sure the exception caught is the one that you're asserting.
assert_raise is part of Test::Unit. It says that the block of code that follows should raise an exception. You got pretty close with your approximation with begin and rescue, so it looks like you understand it fundamentally.

Why doesn't MiniTest::Spec have a wont_raise assertion?

Ruby's Test::Unit has assert_nothing_raised. Test::Unit has been replaced by MiniTest. Why don't MiniTest's assertions / expectations have anything parallel to this? For example you can expect must_raise but not wont_raise.
MiniTest does implement assert_nothing_raised in its Test::Unit compatibility layer, but in its own tests (MiniTest::Unit and MiniTest::Spec) it does not implement any test like this. The reason is, the programmer argues, that testing for nothing raised is not a test of anything; you never expect anything to be raised in a test, except when you are testing for an exception. If an unexpected (uncaught) exception occurs in the code for a test, you'll get an exception reported in good order by the test and you'll know you have a problem.
Example:
require 'minitest/autorun'
describe "something" do
it "does something" do
Ooops
end
end
Output:
Run options: --seed 41521
# Running tests:
E
Finished tests in 0.000729s, 1371.7421 tests/s, 0.0000 assertions/s.
1) Error:
test_0001_does_something(something):
NameError: uninitialized constant Ooops
untitled:5:in `block (2 levels) in <main>'
1 tests, 0 assertions, 0 failures, 1 errors, 0 skips
Which is exactly what you wanted to know. If you were expecting nothing to be raised, you didn't get it and you've been told so.
So, the argument here is: do not use assert_nothing_raised! It's just a meaningless crutch. See, for example:
https://github.com/seattlerb/minitest/issues/70
https://github.com/seattlerb/minitest/issues/159
http://blog.zenspider.com/blog/2012/01/assert_nothing_tested.html
On the other hand, clearly assert_nothing_raised corresponds to some intuition among users, since so many people expect a wont_raise to go with must_raise, etc. In particular one would like to focus an assertion on this, not merely a test. Luckily, MiniTest is extremely minimalist and flexible, so if you want to add your own routine, you can. So you can write a method that tests for no exception and returns a known outcome if there is no exception, and now you can assert for that known outcome.
For example (I'm not saying this is perfect, just showing the idea):
class TestMyRequire < MiniTest::Spec
def testForError # pass me a block and I'll tell you if it raised
yield
"ok"
rescue
$!
end
it "blends" do
testForError do
something_or_other
end.must_equal "ok"
end
end
The point is not that this is a good or bad idea but that it was never the responsibility of MiniTest to do it for you.
If you need it:
# test_helper.rb
module Minitest::Assertions
def assert_nothing_raised(*)
yield
end
end
And to use it:
def test_unknown_setter
assert_nothing_raised do
result.some_silly_column_name = 'value'
end
end
This bothered me enough to dig into the MiniTest sources and provide an implementation in my spec_helper.rb file:
module MiniTest
module Assertions
def refute_raises *exp
msg = "#{exp.pop}.\n" if String === exp.last
begin
yield
rescue MiniTest::Skip => e
return e if exp.include? MiniTest::Skip
raise e
rescue Exception => e
exp = exp.first if exp.size == 1
flunk "unexpected exception raised: #{e}"
end
end
end
module Expectations
infect_an_assertion :refute_raises, :wont_raise
end
end
Hope this proves helpful to someone else who also needs wont_raise. Cheers! :)

Ruby Exceptions -- Why "else"?

I'm trying to understand exceptions in Ruby but I'm a little confused. The tutorial I'm using says that if an exception occurs that does not match any of the exceptions identified by the rescue statements, you can use an "else" to catch it:
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
else
# Other exceptions
ensure
# Always will be executed
end
However, I also saw later in the tutorial "rescue" being used without an exception specified:
begin
file = open("/unexistant_file")
if file
puts "File opened successfully"
end
rescue
file = STDIN
end
print file, "==", STDIN, "\n"
If you can do this, then do I ever need to use else? Or can I just use a generic rescue at the end like this?
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
rescue
# Other exceptions
ensure
# Always will be executed
end
The else is for when the block completes without an exception thrown. The ensure is run whether the block completes successfully or not. Example:
begin
puts "Hello, world!"
rescue
puts "rescue"
else
puts "else"
ensure
puts "ensure"
end
This will print Hello, world!, then else, then ensure.
Here's a concrete use-case for else in a begin expression. Suppose you're writing automated tests, and you want to write a method that returns the error raised by a block. But you also want the test to fail if the block doesn't raise an error. You can do this:
def get_error_from(&block)
begin
block.call
rescue => err
err # we want to return this
else
raise "No error was raised"
end
end
Note that you can't move the raise inside the begin block, because it'll get rescued. Of course, there are other ways without using else, like checking whether err is nil after the end, but that's not as succinct.
Personally, I rarely use else in this way because I think it's rarely needed, but it does come in handy in those rare cases.
EDIT
Another use case occurred to me. Here's a typical begin/rescue:
begin
do_something_that_may_raise_argument_error
do_something_else_when_the_previous_line_doesnt_raise
rescue ArgumentError => e
handle_the_error
end
Why is this less than ideal? Because the intent is to rescue when do_something_that_may_raise_argument_error raises ArgumentError, not when do_something_else_when_the_previous_line_doesnt_raise raises.
It's usually better to use begin/rescue to wrap the minimum code you want to protect from a raise, because otherwise:
you may mask bugs in the code that wasn't supposed to raise
the intention of rescue is harder to decipher. Someone (including your future self) may read the code and wonder "Which expression did I want to protect? It looks like expression ABC... but maybe expression DEF too???? What was the author intending?!" Refactoring becomes much more difficult.
You avoid those problems with this simple change:
begin
do_something_that_may_raise_argument_error
rescue ArgumentError => e
handle_the_error
else
do_something_else_when_the_previous_line_doesnt_raise
end
The else block in a begin rescue end block is used when you are perhaps expecting an exception of some sort to occur. If you run through all of your expected exceptions but still have nothing raised, then in your else block you can do whatever's needed now that you know that your original code ran error free.
The only reason I can see for the else block is if you want to execute something before the ensure block when the code in the begin block didn't raise any errors.
begin
puts "Hello"
rescue
puts "Error"
else
puts "Success"
ensure
puts "my old friend"
puts "I've come to talk with you again."
end
Thanks to else you sometimes can merge two nested begin end blocks.
So (simplified example from my current code) instead of:
begin
html = begin
NetHTTPUtils.request_data url
rescue NetHTTPUtils::Error => e
raise unless 503 == e.code
sleep 60
retry
end
redo unless html["market"]
end
you write:
begin
html = NetHTTPUtils.request_data url
rescue NetHTTPUtils::Error => e
raise unless 503 == e.code
sleep 60
retry
else
redo unless html["market"]
end

Resources