2013-01-23 44 views
1

我的自定义异常类:如何测试自定义异常,消息与Python鼻子

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) 

我的测试

# 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() 

这里就是鼻子吐出回来了

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) 

所以...

我的问题是双重的:

  • 如何解决这个问题?
  • 如何测试(单独地或完全):
    • 异常类型
    • Exception.status
    • Exception.uri
    • Exception.msg

解决方案:用来自alynna的帮助(如下)

这很好。

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

回答

1

Nose的@raises装饰器不支持检查除异常类以外的任何其他内容。传递更多参数意味着您想允许更多类型的异常被解释为有效。因此,鼻子会将您传递的字符串解释为例外,并且找不到它是__name__。 (见docs

要解决它,你可以实现额外的装饰为您定制的异常,如:

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 

,然后使用它像@raises_custom(msg="HTTP ERROR 403: Invalid User")

(我没有测试上面的代码,只是为了给它怎么可能看起来像一个粗略的轮廓)

更新:使用assertRaises,作为alynna的建议,可能是更清洁,但。特别是如果你能识别一个特殊的地方发生异常会更好 - 而不是用装饰器来包装整个函数。

+0

这很酷!谢谢。 – doremi