unittest
的Python文档暗示assertRaises()
方法可以用作上下文管理器。下面的代码给出了一个Python文档中单元测试的简单示例。在assertRaises()
方法中的testsample()
调用工作正常。
现在我想在引发异常时访问异常,但是如果我将它注释掉,而是取消注释我尝试使用上下文管理器的下一个块,当我尝试执行代码时,我得到一个AttributeError: __exit__
。这适用于Python 2.7.2和3.2.2。我可以在try...except
块中捕获异常并以这种方式访问它,但unittest的文档似乎暗示上下文管理器也会这样做。
还有别的我在这里做错了吗?
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
self.seq = [x for x in range(10)]
def testshuffle(self):
# make sure the shuffled sequence does not lose any elements
random.shuffle(self.seq)
self.seq.sort()
self.assertEqual(self.seq, [x for x in range(10)])
def testchoice(self):
element = random.choice(self.seq)
self.assert_(element in self.seq)
def testsample(self):
self.assertRaises(ValueError, random.sample, self.seq, 20)
# with self.assertRaises(ValueError, random.sample, self.seq, 20):
# print("Inside cm")
for element in random.sample(self.seq, 5):
self.assert_(element in self.seq)
if __name__ == '__main__':
unittest.main()
source code for unittest没有显示assertRaises的异常钩子:
class _AssertRaisesContext(object):
"""A context manager used to implement TestCase.assertRaises* methods."""
def __init__(self, expected, test_case, expected_regexp=None):
self.expected = expected
self.failureException = test_case.failureException
self.expected_regexp = expected_regexp
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
if exc_type is None:
try:
exc_name = self.expected.__name__
except AttributeError:
exc_name = str(self.expected)
raise self.failureException(
"{0} not raised".format(exc_name))
if not issubclass(exc_type, self.expected):
# let unexpected exceptions pass through
return False
self.exception = exc_value # store for later retrieval
if self.expected_regexp is None:
return True
expected_regexp = self.expected_regexp
if isinstance(expected_regexp, basestring):
expected_regexp = re.compile(expected_regexp)
if not expected_regexp.search(str(exc_value)):
raise self.failureException('"%s" does not match "%s"' %
(expected_regexp.pattern, str(exc_value)))
return True
因此,正如您所怀疑的那样,如果您想要在保持assertRaises测试的同时拦截异常,那么构建您自己的try / except块是可行的方法:
def testsample(self):
with self.assertRaises(ValueError):
try:
random.sample(self.seq, 20)
except ValueError as e:
# do some action with e
self.assertEqual(e.args,
('sample larger than population',))
# now let the context manager do its work
raise
似乎没有人提出:
import unittest
# For python < 2.7, do import unittest2 as unittest
class Class(object):
def should_raise(self):
raise ValueError('expected arg')
class test_Class(unittest.TestCase):
def test_something(self):
DUT = Class()
with self.assertRaises(ValueError) as exception_context_manager:
DUT.should_raise()
exception = exception_context_manager.exception
self.assertEqual(exception.args, ('expected arg', ))
我通常使用e_cm作为exception_context_manager的缩写。
根据文件:
如果使用省略的callableObj或None调用,则返回上下文对象
所以代码应该是:
with self.assertRaises(ValueError):
random.sample(self.seq, 20)
考虑到这是六年前的问题,我想这是现在有效但当时没有用的东西。 docs国家出现在2.7但不是微型版本。
import unittest
class TestIntParser(unittest.TestCase):
def test_failure(self):
failure_message = 'invalid literal for int() with base 10'
with self.assertRaises(ValueError) as cm:
int('forty two')
self.assertIn(failure_message, cm.exception.message)
if __name__ == '__main__':
unittest.main()