Reputation: 15329
My custom exception classes:
class MyCustomException(Exception):
pass
class MyCustomRestException(MyCustomException):
def __init__(self, status, uri, msg=""):
self.uri = uri
self.status = status
self.msg = msg
super(MyCustomException, self).__init__(msg)
def __str__(self):
return "HTTP ERROR %s: %s \n %s" % (self.status, self.msg, self.uri)
My Test
# note: @raises(MyCustomRestException) works by itself
@raises(MyCustomRestException, 'HTTP ERROR 403: Invalid User')
def test_bad_token():
sc = SomeClass('xxx', account_number)
result = ec.method_that_generates_exception()
Here's what nose spits back out
12:52:13 ~/sandbox/ec$ nosetests -v
Failure: AttributeError ('str' object has no attribute '__name__') ... ERROR
======================================================================
ERROR: Failure: AttributeError ('str' object has no attribute '__name__')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/nose/loader.py", line 390, in loadTestsFromName
addr.filename, addr.module)
File "/usr/local/lib/python2.7/site-packages/nose/importer.py", line 39, in importFromPath
return self.importFromDir(dir_path, fqname)
File "/usr/local/lib/python2.7/site-packages/nose/importer.py", line 86, in importFromDir
mod = load_module(part_fqname, fh, filename, desc)
File "/ec/tests/my_client_tests.py", line 16, in <module>
@raises(MyCustomRestException, 'HTTP ERROR 403: Invalid User')
File "/usr/local/lib/python2.7/site-packages/nose/tools/nontrivial.py", line 55, in raises
valid = ' or '.join([e.__name__ for e in exceptions])
AttributeError: 'str' object has no attribute '__name__'
----------------------------------------------------------------------
Ran 1 test in 0.012s
FAILED (errors=1)
So ...
My questions is two fold:
Solution: with help from alynna (below)
This works great.
def test_bad_token():
sc = SomeClass('xxx', account_number)
with assert_raises(MyCustomRestException) as e:
sc.method_that_generates_exception()
assert_equal(e.exception.status, 403)
assert_equal(e.exception.msg, 'Invalid User')
Upvotes: 2
Views: 3962
Reputation: 3066
Nose's @raises
decorator doesn't support checking anything else than exception's class. Passing more arguments means you want to allow more types of exceptions to be interpreted as valid. Hence Nose interprets the string you're passing as a exception and can't find it's __name__
. (see docs)
To fix it, you could to implement additional decorator for your custom exception, like:
from nose.tools import eq_
from functools import wraps
def raises_custom(status=None, uri=None, msg=None):
assert status or uri or msg, 'You need to pass either status, uri, or msg'
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
try:
function(*args, **kwargs)
except MyCustomException, e:
def check(value, name):
if value:
eq_(getattr(exception, name), value)
check(status, 'status')
check(uri, 'uri')
check(msg, 'msg')
except:
raise
else:
message = "%{} did not raise MyCustomException".format(\
function.__name__)
raise AssertionError(message)
return wrapper
return decorator
and then use it like @raises_custom(msg="HTTP ERROR 403: Invalid User")
.
(I didn't test the code above, just meant to give a rough outline of how it could look like)
UPDATE: Using assertRaises, as alynna's suggested, is probably cleaner though. Especially it's better if you can identify a specific place where exception should occur - as opposed to wrapping whole function with a decorator.
Upvotes: 1
Reputation: 2671
I think your problem is that the arguments to the @raises decorator are expected to all be exception classes: https://nose.readthedocs.org/en/latest/testing_tools.html#nose.tools.raises
You might want assertRaises instead. The documentation shows it being used to test extra attributes of exceptions: http://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises
Upvotes: 2