![](/img/trans.png)
[英]Error while testing the raise of self-defined exceptions (using assertRaises())
[英]using assertRaises - handling propagated exceptions
我有一些代碼正在測試包裝的異常,當它失敗並且異常傳播時,我認為錯誤消息和追溯信息不夠詳細,主要是因為它沒有告訴我與測試相比預期的結果,我想了解例外情況和期望的詳細信息。
我調整了測試(請參見下面的示例代碼)。 我想知道這種方法是否有效,是否有任何Python測試或模擬框架可以直接實現? (當前我正在使用unittest和mox)
該問題的答案之一簡單地涉及了在這種情況下使用self.fail的適當性,但並未詳細說明。 我的假設是,如果我嘗試將測試限制在一個區域,則可以通過測試。
注意:如果您運行該代碼示例,該示例將失敗,以演示我希望看到的行為。 我正在使用Python 2.7,Mox 0.5.3
import sys
import urllib2
from contextlib import closing
try:
import lxml.etree as ET
except ImportError:
import xml.etree.ElementTree as ET
class Defect(Exception):
"""Wrapped exception, for module error detection"""
def __init__(self, *args):
Exception.__init__(self, *args)
self.wrapped_exc = sys.exc_info()
class StudioResources:
"""Dummy class"""
def _opener(self, request, html=False):
with closing(urllib2.urlopen(request)) as response:
try:
if html:
import lxml.html
return lxml.html.parse(response)
else:
return ET.parse(response)
except urllib2.HTTPError, e:
if e.code in [400, 500]: # Bad Request, Internal Server Error
raise Defect, "report error to the library maintainer"
else:
raise
###
# Tests
###
import unittest
import mox
import traceback
import difflib
import urllib
import httplib
def format_expectation(exc_expected=None, exc_instance=None):
"""Synopsis - For exceptions, inspired by _AssertRaisesContext
try:
self.assertRaises(myexc, self.studio._opener, None)
except Exception, e:
self.fail(format_expectation(exc_expected=myexc, exc_instance=e))
"""
if not isinstance(exc_expected, type) or exc_instance is None:
raise ValueError, "check __init__ args"
differ = difflib.Differ()
inst_class = exc_instance.__class__
def fullname(c): return "%s.%s" % (c.__module__, c.__name__)
diff = differ.compare(
(fullname(inst_class),), (fullname(exc_expected),))
_str = ("Unexpected Exception type. unexpected:- expected:+\n%s"
% ("\n".join(diff),))
return _str
class StudioTest(mox.MoxTestBase):
def setUp(self):
mox.MoxTestBase.setUp(self)
self.studio = StudioResources()
def test_opener_defect(self):
f = urllib.addinfourl(urllib2.StringIO('dummy'), None, None)
RESP_CODE = 501
self.mox.StubOutWithMock(f, 'read')
self.mox.StubOutWithMock(urllib2, 'urlopen')
urllib2.urlopen(mox.IgnoreArg()).AndReturn(f)
f.read(mox.IgnoreArg()).AndRaise(urllib2.HTTPError(
'http://c.com', RESP_CODE, httplib.responses[RESP_CODE], "", None))
self.mox.ReplayAll()
try:
with self.assertRaises(Defect) as exc_info:
self.studio._opener(None)
except Exception, e:
traceback.print_exc()
self.fail(format_expectation(exc_expected=Defect, exc_instance=e))
# check the response code
exc, inst, tb = exc_info.exception.wrapped_exc
self.assertEquals(inst.code, RESP_CODE)
self.mox.VerifyAll()
if __name__ == '__main__':
unittest.main()
在編寫單元測試時,將測試限制為一件事總是一個好主意。 我看不到您的代碼有什么問題,但我會將所有內容包裝在上下文管理器中。 我使用鼻子而不是unittest,它將任何AssertionError都視為失敗(這意味着不需要調用self.fail()
),並且我編寫了自己的上下文管理器來處理這種情況。 如果您有興趣,請參見以下代碼:
class assert_raises:
def __init__(self, exception):
self.exception = exception
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
assert exc_type is self.exception, "Got '{}', expected '{}'"\
.format('None' if exc_type is None else exc_type.__name__,
self.exception.__name__)
return True
然后如以下示例所示使用它:
>>> with assert_raised(ValueError):
... raise ValueError
>>> with assert_raised(ValueError):
... pass
Traceback (most recent call last):
...
AssertionError: Got 'None', expected 'ValueError'
>>> with assert_raised(ValueError):
... raise TypeError
Traceback (most recent call last):
...
AssertionError: Got 'TypeError', expected 'ValueError'
由於引發了AssertionError,鼻子將其視為失敗,並且無論如何都會打印完整的回溯。 這是為鼻子設計的,但是要針對單元測試和mox進行調整將是一件微不足道的事情。 如果您不太擔心故障的確切模式,您甚至可以按原樣使用它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.