如何在Python2.7的unittest中显示由assertRaises()捕获的错误消息?

为了确保来自我的模块的错误消息是有用的,我想看到所有由assertRaises()捕获的错误消息。 今天我为每个assertRaises()做了这个工作,但是由于testing代码中有很多这样的代码,所以它非常繁琐。

我怎样才能打印所有assertRaises()的错误消息? 我研究了http://docs.python.org/library/unittest.html上的文档,却没有弄清楚如何解决这个问题。 我可以以某种方式monkeypatch assertRaises()方法? 我不想更改testing代码中的所有assertRaises()行,因为我经常使用testing代码的标准方式。

我想这个问题与Pythonunit testing有关:我如何testingexception中的参数?

我今天就是这么做的。 例如:

#!/usr/bin/env python def fail(): raise ValueError('Misspellled errrorr messageee') 

和testing代码:

 #!/usr/bin/env python import unittest import failure class TestFailureModule(unittest.TestCase): def testFail(self): self.assertRaises(ValueError, failure.fail) if __name__ == '__main__': unittest.main() 

为了检查错误信息,我只是将assertRaises()中的错误types更改为IOError。 然后我可以看到错误信息:

  E ====================================================================== ERROR: testFail (__main__.TestFailureModule) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_failure.py", line 8, in testFail self.assertRaises(IOError, failure.fail) File "/usr/lib/python2.7/unittest/case.py", line 471, in assertRaises callableObj(*args, **kwargs) File "/home/jonas/Skrivbord/failure.py", line 4, in fail raise ValueError('Misspellled errrorr messageee') ValueError: Misspellled errrorr messageee ---------------------------------------------------------------------- Ran 1 test in 0.001s FAILED (errors=1) 

有什么build议么? /乔纳斯

编辑:

从罗伯特·罗斯尼的提示,我设法解决这个问题。 它主要不是用于拼写错误,而是用于确保错误信息对模块用户真正有意义。 unittest的正常function(这是我大部分时间使用它的方法)是通过设置SHOW_ERROR_MESSAGES = False来实现的。

我简单地覆盖assertRaises()方法,如下所示。 它的作品像魅力!

 SHOW_ERROR_MESSAGES = True class NonexistantError(Exception): pass class ExtendedTestCase(unittest.TestCase): def assertRaises(self, excClass, callableObj, *args, **kwargs): if SHOW_ERROR_MESSAGES: excClass = NonexistantError try: unittest.TestCase.assertRaises(self, excClass, callableObj, *args, **kwargs) except: print '\n ' + repr(sys.exc_info()[1]) 

结果输出的一小部分:

 testNotIntegerInput (__main__.TestCheckRegisteraddress) ... TypeError('The registeraddress must be an integer. Given: 1.0',) TypeError("The registeraddress must be an integer. Given: '1'",) TypeError('The registeraddress must be an integer. Given: [1]',) TypeError('The registeraddress must be an integer. Given: None',) ok testCorrectNumberOfBytes (__main__.TestCheckResponseNumberOfBytes) ... ok testInconsistentLimits (__main__.TestCheckNumerical) ... ValueError('The maxvalue must not be smaller than minvalue. Given: 45 and 47, respectively.',) ValueError('The maxvalue must not be smaller than minvalue. Given: 45.0 and 47.0, respectively.',) ok testWrongValues (__main__.TestCheckRegisteraddress) ... ValueError('The registeraddress is too small: -1, but minimum value is 0.',) ValueError('The registeraddress is too large: 65536, but maximum value is 65535.',) ok testTooShortString (__main__.TestCheckResponseWriteData) ... ValueError("The payload is too short: 2, but minimum value is 4. Given: '\\x00X'",) ValueError("The payload is too short: 0, but minimum value is 4. Given: ''",) ValueError("The writedata is too short: 1, but minimum value is 2. Given: 'X'",) ValueError("The writedata is too short: 0, but minimum value is 2. Given: ''",) ok testKnownValues (__main__.TestCreateBitPattern) ... ok testNotIntegerInput (__main__.TestCheckSlaveaddress) ... TypeError('The slaveaddress must be an integer. Given: 1.0',) TypeError("The slaveaddress must be an integer. Given: '1'",) TypeError('The slaveaddress must be an integer. Given: [1]',) TypeError('The slaveaddress must be an integer. Given: None',) ok 

开箱即用的unittest不会这样做。 如果这是你想经常做的事情,你可以尝试这样的事情:

 class ExtendedTestCase(unittest.TestCase): def assertRaisesWithMessage(self, msg, func, *args, **kwargs): try: func(*args, **kwargs) self.assertFail() except Exception as inst: self.assertEqual(inst.message, msg) 

ExtendedTestCase派生你的unit testing类而不是unittest.TestCase

但是,如果你只关心拼错的错误信息,并且足够关心想要围绕它构buildtesting用例,则不应该将消息内联为string文字。 你应该对他们做什么与其他重要的string:把它们定义为你导入的模块中的常量,并且有人负责校对。 在他的代码中拼错单词的开发者也会在他的testing用例中拼错它们。

我曾经喜欢罗伯特·罗斯尼(Roberto Rossney)上面给出的最优秀的答案。 现在,我更喜欢使用assertRaises作为上下文pipe理器(unittest2中的新function),如下所示:

 with self.assertRaises(TypeError) as cm: failure.fail() self.assertEqual( 'The registeraddress must be an integer. Given: 1.0', str(cm.exception) ) 

您正在寻找自Python 2.7以来可用的assertRaisesRegexp 。 从文档:

 self.assertRaisesRegexp(ValueError, 'invalid literal for.*XYZ$', int, 'XYZ') 

要么:

 with self.assertRaisesRegexp(ValueError, 'literal'): int('XYZ') 

mkelley33给出了很好的答案,但这种方法可以通过一些代码分析工具(如Codacy)来检测。 问题是,它不知道assertRaises可以用作上下文pipe理器,它报告并不是所有的参数都传递给了assertRaises 方法

所以,我想提高Robert Rossney的答案:

 class TestCaseMixin(object): def assertRaisesWithMessage(self, exception_type, message, func, *args, **kwargs): try: func(*args, **kwargs) except exception_type as e: self.assertEqual(e.args[0], message) else: self.fail('"{0}" was expected to throw "{1}" exception' .format(func.__name__, exception_type.__name__)) 

主要区别是:

  1. 我们testingexception的types。
  2. 我们可以在Python 2和Python 3上运行这个代码(我们称之为e.args[0]因为Py3中的错误没有message属性)。

如果你想错误消息完全匹配的东西:

 with self.assertRaises(ValueError) as error: do_something() self.assertEqual(error.exception.message, 'error message')