简体   繁体   English

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

[英]How to show the error messages caught by assertRaises() in unittest in Python2.7?

In order to make sure that the error messages from my module are informative, I would like to see all the error messages caught by assertRaises().为了确保来自我的模块的错误消息是有用的,我想查看 assertRaises() 捕获的所有错误消息。 Today I do it for each assertRaises(), but as there are lots of them in the test code it gets very tedious.今天我为每个 assertRaises() 都做,但是由于测试代码中有很多它们变得非常乏味。

How can I print the error messages for all the assertRaises()?如何打印所有 assertRaises() 的错误消息? I have studied the documentation on http://docs.python.org/library/unittest.html without figuring out how to solve it.我已经研究了http://docs.python.org/library/unittest.html上的文档,但没有弄清楚如何解决它。 Can I somehow monkeypatch the assertRaises() method?我可以以某种方式修补 assertRaises() 方法吗? I prefer not to change all the assertRaises() lines in the test code, as I most often use the test code the standard way.我不想更改测试代码中的所有 assertRaises() 行,因为我最常以标准方式使用测试代码。

I guess this question is related to Python unittest: how do I test the argument in an Exceptions?我猜这个问题与Python 单元测试有关:如何在异常中测试参数?

This is how I do it today.这就是我今天的做法。 For example:例如:

#!/usr/bin/env python

def fail():
    raise ValueError('Misspellled errrorr messageee')

And the test code:和测试代码:

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

To check the error message, I simply change the error type in the assertRaises() to for example IOError.要检查错误消息,我只需将 assertRaises() 中的错误类型更改为例如 IOError。 Then I can see the error message:然后我可以看到错误消息:

 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)

Any suggestions?有什么建议么? /Jonas /乔纳斯

EDIT:编辑:

With the hints from Robert Rossney I managed to solve the problem.在罗伯特罗斯尼的提示下,我设法解决了这个问题。 It is not mainly intended for spelling errors, but for making sure that the error messages are really meaningful for the user of the module.它不是主要用于拼写错误,而是用于确保错误消息对模块用户真正有意义。 The normal functionality of unittest (this is how I use it most of the time) is achieved by setting SHOW_ERROR_MESSAGES = False. unittest 的正常功能(这是我大部分时间使用它的方式)是通过设置 SHOW_ERROR_MESSAGES = False 来实现的。

I simply override the assertRaises() method, as seen below.我只是覆盖了 assertRaises() 方法,如下所示。 It works like charm!它就像魅力一样!

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

A fraction of the resulting output:结果输出的一小部分:

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

I once preferred the most excellent answer given above by @Robert Rossney.我曾经更喜欢@Robert Rossney 上面给出的最优秀的答案。 Nowadays, I prefer to use assertRaises as a context manager (a new capability in unittest2) like so:现在,我更喜欢使用 assertRaises 作为上下文管理器(unittest2 中的新功能),如下所示:

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

You are looking for assertRaisesRegex , which is available since Python 3.2.您正在寻找assertRaisesRegex ,它自 Python 3.2 起可用。 From the docs:从文档:

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

or:要么:

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

PS: if you are using Python 2.7, then the correct method name is assertRaisesRegexp . PS:如果您使用的是 Python 2.7,那么正确的方法名称是assertRaisesRegexp

Out-of-the-box unittest doesn't do this.外的现成unittest并没有这样做。 If this is something you want to do frequently, you can try something like this:如果这是你想要经常做的事情,你可以尝试这样的事情:

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)

Derive your unit test classes from ExtendedTestCase instead of unittest.TestCase .ExtendedTestCase而不是unittest.TestCase派生单元测试类。

But really, if you're simply concerned about misspelled error messages, and concerned enough to want to build test cases around it, you shouldn't be inlining messages as string literals.但实际上,如果您只是担心拼写错误的错误消息,并且非常担心想要围绕它构建测试用例,那么您不应该将消息内联为字符串文字。 You should do with them what you do with any other important strings: defining them as constants in a module that you import and that someone is responsible for proofreading.您应该像处理任何其他重要字符串一样处理它们:在您导入的模块中将它们定义为常量,并且有人负责校对。 A developer who misspells words in his code will also misspell them in his test cases.在他的代码中拼错单词的开发人员也会在他的测试用例中拼错。

If you want the error message exactly match something:如果您希望错误消息与某些内容完全匹配:

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

mkelley33 gives nice answer, but this approach can be detected as issue by some code analysis tools like Codacy . mkelley33 给出了很好的答案,但这种方法可以被一些代码分析工具(如Codacy)检测为问题。 The problem is that it doesn't know that assertRaises can be used as context manager and it reports that not all arguments are passed to assertRaises method .问题是它不知道assertRaises可以用作上下文管理器,并且它报告并非所有参数都传递给assertRaises方法

So, I'd like to improve Robert's Rossney answer:所以,我想改进罗伯特的罗斯尼回答:

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

Key differences are:主要区别是:

  1. We test type of exception.我们测试异常类型。
  2. We can run this code both on Python 2 and Python 3 (we call e.args[0] because errors in Py3 don't have message attribute).我们可以在 Python 2 和 Python 3 上运行这段代码(我们称之为e.args[0]因为 Py3 中的错误没有message属性)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM