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

jon*_*erg 69 python unit-testing

为了确保来自我的模块的错误消息提供信息,我希望看到assertRaises()捕获的所有错误消息.今天我为每个assertRaises()执行此操作,但由于测试代码中有很多这样的内容,因此非常繁琐.

如何打印所有assertRaises()的错误消息?我已经研究了http://docs.python.org/library/unittest.html上的文档,但没有弄清楚如何解决它.我可以以某种方式monkeypatch assertRaises()方法?我不想更改测试代码中的所有assertRaises()行,因为我经常以标准方式使用测试代码.

我想这个问题与Python unittest有关:如何在Exceptions中测试参数?

这就是我今天的表现.例如:

#!/usr/bin/env python

def fail():
    raise ValueError('Misspellled errrorr messageee')
Run Code Online (Sandbox Code Playgroud)

和测试代码:

#!/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()  
Run Code Online (Sandbox Code Playgroud)

要检查错误消息,我只需将assertRaises()中的错误类型更改为例如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)
Run Code Online (Sandbox Code Playgroud)

有什么建议?/乔纳斯

编辑:

借助Robert Rossney的提示,我设法解决了这个问题.它主要不是用于拼写错误,而是用于确保错误消息对模块用户真正有意义.通过设置SHOW_ERROR_MESSAGES = False来实现unittest的正常功能(这是我大多数时间使用它的方式).

我只是覆盖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]) 
Run Code Online (Sandbox Code Playgroud)

结果输出的一小部分:

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
Run Code Online (Sandbox Code Playgroud)

mke*_*y33 106

我曾经更喜欢@Robert Rossney给出的最优秀答案.如今,我更喜欢使用assertRaises作为上下文管理器(unittest2中的新功能),如下所示:

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

  • 注意 assertRaises可以用作Python 2.7中“ unittest”的上下文管理器。unittest2向后移植了Python早期版本的功能。http://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises (2认同)

Iod*_*nas 51

您正在寻找assertRaisesRegexp,它自Python 2.7起可用.来自文档:

self.assertRaisesRegexp(ValueError, 'invalid literal for.*XYZ$', int, 'XYZ')
Run Code Online (Sandbox Code Playgroud)

要么:

with self.assertRaisesRegexp(ValueError, 'literal'):
    int('XYZ')
Run Code Online (Sandbox Code Playgroud)

  • 在python 3.6上,它说`DeprecationWarning:请与`self.assertRaisesRegexp(RuntimeError,'... regex ...')一起使用时,请改用assertRaisesRegex`。 (2认同)

Rob*_*ney 45

开箱即用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)
Run Code Online (Sandbox Code Playgroud)

从而ExtendedTestCase不是派生您的单元测试类unittest.TestCase.

但实际上,如果您只是担心拼写错误的错误消息,并且足够关注想要围绕它构建测试用例,那么您不应该将消息内联为字符串文字.您应该使用它们对任何其他重要字符串执行的操作:在导入的模块中将它们定义为常量,并且有人负责校对.在他的代码中拼错单词的开发人员也会在他的测试用例中拼错它们.

  • "在他的代码中拼错单词的开发人员也会在他的测试用例中拼错它们". (14认同)
  • 对我来说,当你测试看到发生_specific_错误时,它会更加令人震惊,但是由于意外的副作用,测试可能会"通过".例如,您预期的错误没有引发,但在其他地方引发相同的错误类型,从而满足测试.测试通过,代码被窃听.对于您正在寻找的错误的子类化错误也是同样的 - 如果您的测试过于笼统,您最终会捕获您不期望的错误. (11认同)
  • 这不是真的,开箱即用的单位测试确实这样做了. (3认同)
  • 您应该使用 ``inst.args[0]`` 而不是 ``inst.message`` 在 Python 2 和 Python 3 上运行此代码 (2认同)

yal*_* du 23

如果您希望错误消息与以下内容完全匹配:

with self.assertRaises(ValueError) as error:
  do_something()
self.assertEqual(error.exception.message, 'error message')
Run Code Online (Sandbox Code Playgroud)

  • 我必须使用“str(error.exception)”而不是“error.exception.message”,因为在我的情况下,“error.exception”没有“message”属性。 (11认同)

obl*_*lex 5

mkelley33给出了很好的答案,但是这种方法可以被像Codacy这样的代码分析工具检测为问题.问题是它不知道assertRaises可以用作上下文管理器,它报告并非所有参数都传递给assertRaises 方法.

所以,我想改进罗伯特的罗斯尼回答:

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__))
Run Code Online (Sandbox Code Playgroud)

主要区别是:

  1. 我们测试例外的类型.
  2. 我们可以在Python 2和Python 3上运行此代码(我们调用e.args[0]因为Py3中的错误没有 message属性).