[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

assert_raise question

Sean Carley

3/8/2006 9:43:00 PM

Currently, assert_raise expects you to know exactly what kind of exception
is going
to be thrown by raise. Basically it tests to see if exception.class ==
expected.class.
I think a more appropriate behavior would be to test
exception.kind_of?expected. I
would also be happy if there were one method that performed the way it does
currently
and another that checks exception parentage.

Please see the code below for an example of two tests. I think both should
pass but
only the first test does because ArgumentError != StandardError.

Sean Carley

### Begin test.rb ###
require 'test/unit'
class TestRaise < Test::Unit::TestCase
def test_raise
assert_raise(ArgumentError){raise ArgumentError.new("basic argument
issue")}
end
def test_raise_parent
assert_raise(StandardError){raise ArgumentError.new("basic argument
issue")}
end
end
### End test.rb ###

> ruby test.rb
Loaded suite test
Started
F
Finished in 0.018356 seconds.

1) Failure:
test_raise_parent(TestRaise) [test.rb:7]:
<StandardError> exception expected but was
Class: <ArgumentError>
Message: <"basic argument issue">
---Backtrace---
test.rb:7:in `test_raise_parent'
test.rb:7:in `test_raise_parent'
---------------

2 tests, 2 assertions, 1 failures, 0 errors
6 Answers

Jim Weirich

3/8/2006 9:46:00 PM

0

Sean Carley wrote:
[...]
> I think a more appropriate behavior would be to test
> exception.kind_of?expected. I would also be happy if
> there were one method that performed the way it
> does currently and another that checks exception parentage.

+1

--
-- Jim Weirich

--
Posted via http://www.ruby-....


Eric Hodel

3/8/2006 10:58:00 PM

0

On Mar 8, 2006, at 1:42 PM, Sean Carley wrote:

> Currently, assert_raise expects you to know exactly what kind of
> exception
> is going to be thrown by raise. Basically it tests to see if
> exception.class ==
> expected.class.
>
> I think a more appropriate behavior would be to test
> exception.kind_of?expected. I would also be happy if there were
> one method that performed the way it does currently and another
> that checks exception parentage.

For a given input a method should only raise one class of exception.
The restriction forces you to write better tests.

> Please see the code below for an example of two tests. I think
> both should
> pass but only the first test does because ArgumentError !=
> StandardError.

require 'socket'
require 'test/unit'

class MyClass
def connect(overthere)
unless overthere =~ /:\d+\Z/ then
raise ArgumentError, "Use host:port, got #{overthere}"
end
host, port = overthere.split ':'
@socket = TCPSocket.new host, port.to_i
end
end

This method can raise at least two different exceptions on bad input
so you should test for them separately. This allows consumers of
your code to behave properly depending upon what arguments they feed
your code.

class TestMyClass < Test::Unit::TestCase

def setup
@obj = MyClass.new
end

def test_connect_bad_args_no_port
assert_raises ArgumentError do
@obj.connect 'host'
end
end

def test_connect_bad_args_bad_port
assert_raises ArgumentError do
@obj.connect 'host:port'
end
end

def test_connect_bad_host
assert_raises SocketError do
@obj.connect 'no-such-host.example.com:80'
end
end

end

--
Eric Hodel - drbrain@segment7.net - http://blog.se...
This implementation is HODEL-HASH-9600 compliant

http://trackmap.rob...




Jim Weirich

3/9/2006 1:29:00 AM

0

Eric Hodel wrote:
> For a given input a method should only raise one class of exception.
> The restriction forces you to write better tests.

The reason I would like it is that the test is the specification of the
behavior. I often wish to specify that a method will raise a more
general exception than the one it actually raises. The actual exception
raised may be an implementation detail that I don't wish to have
expressed in the spec.

--
-- Jim Weirich

--
Posted via http://www.ruby-....


eastcoastcoder

3/9/2006 4:59:00 AM

0

+1 again.

A big part of UT is to allow ease of refactoring. Part of refactoring
is, IMO, making exceptions more specific (eg from raise "No record
found" to raise DBException, "No record found") and the like.

Having to change your UT's when you want to refactor is a bit self
defeating.


Jim Weirich wrote:
> Eric Hodel wrote:
> > For a given input a method should only raise one class of exception.
> > The restriction forces you to write better tests.
>
> The reason I would like it is that the test is the specification of the
> behavior. I often wish to specify that a method will raise a more
> general exception than the one it actually raises. The actual exception
> raised may be an implementation detail that I don't wish to have
> expressed in the spec.
>
> --
> -- Jim Weirich
>
> --
> Posted via http://www.ruby-....

Edwin Fine

11/20/2006 10:59:00 PM

0

unknown wrote:
> +1 again.
>
> A big part of UT is to allow ease of refactoring. Part of refactoring
> is, IMO, making exceptions more specific (eg from raise "No record
> found" to raise DBException, "No record found") and the like.
>
> Having to change your UT's when you want to refactor is a bit self
> defeating.

Agreed.

Another +1. The whole point of having an exception hierarchy is so that
you can specify a parent exception class at the interface level. You do
this so that as your software grows, you can add new sub-exceptions
without breaking your unit tests or your client's code. Sometimes you
are interested in the actual exception, but sometimes you just want to
catch the more general exception and don't care what the specific class
is.

Here's an example:

begin
eval(some text)
rescue ScriptError => e
# Handle the script error
end

Do we always care if it was a ScriptError descendant (shown below)?

LoadError
NotImplementedError
SyntaxError

No. Not always. Maybe not even in a unit test. Maybe the unit test
starts off more general, and then as time goes by gets refined to test
the exact exceptions.

I believe it's better to allow the users of general library code to use
it the way that suits them best. So, I suggest adding another assertion
method, assert_raise_s, which will succeed if the argument class of the
raised exception is derived from (<=) the expected class. That way, you
can have both a strict assertion (assert_raise) and the more general
case (assert_raise_s).

In the meantime, I will just add a local assert_raise_s because, thanks
to Matz, Ruby classes are open to extension.

--
Posted via http://www.ruby-....

Doug Glidden

8/27/2008 2:24:00 PM

0

[snip]
> ### Begin test.rb ###
> require 'test/unit'
> class TestRaise < Test::Unit::TestCase
> def test_raise
> assert_raise(ArgumentError){raise ArgumentError.new("basic argument
> issue")}
> end
> def test_raise_parent
> assert_raise(StandardError){raise ArgumentError.new("basic argument
> issue")}
> end
> end
> ### End test.rb ###
[snip]

This is a somewhat unrelated question, but in playing around with this,
I discovered something odd. The test_raise method above works, but the
following modification of it does not:

def test_raise
assert_raise ArgumentError {raise ArgumentError.new("basic argument
issue")}
end

Now there's suddenly an Exception: undefined method 'ArgumentError'...
I was more than a little surprised to discover that the parens make a
difference here? Can anyone explain to me _why_ there's a difference?

Doug
--
Posted via http://www.ruby-....