How to test custom exception, message with Python Nose

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:

  • How do I fix this error?
  • How can I test (individually or altogether):
    • Exception Type
    • Exception.status
    • Exception.uri
    • Exception.msg

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')

Answers


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


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.


Need Your Help

ruby passing value into array using send

ruby

I am new to ruby , can someone explain what the second and 3rd line do?

List of popular importable calendar formats?

file import calendar standards

Say I wanted to write a program that would export calendar data so that it could be imported into another calendar application. What would be some popular formats for that, and where could I find

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.