【问题标题】:How to show the error messages caught by assertRaises() in unittest in Python2.7?如何在Python2.7的unittest中显示assertRaises()捕获的错误信息?
【发布时间】:2012-01-30 03:50:53
【问题描述】:

为了确保来自我的模块的错误消息提供信息,我想查看 assertRaises() 捕获的所有错误消息。今天我对每个 assertRaises() 都做,但是由于测试代码中有很多,所以变得非常乏味。

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

我猜这个问题和Python unittest: how do I test the argument in an Exceptions?有关

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

#!/usr/bin/env python

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

以及测试代码:

#!/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() 中的错误类型更改为例如 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)

有什么建议吗? /乔纳斯

编辑:

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

结果输出的一小部分:

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

【问题讨论】:

  • 如果需要检查参数,为什么还要继续使用 assertRaises?为什么不简单地捕获异常并使用tryexcept 对其进行检查?

标签: python unit-testing


【解决方案1】:

我曾经更喜欢@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)
)

【讨论】:

  • 注意。 assertRaises 可以用作 Python 2.7 中“unittest”的上下文管理器。 unittest2 为早期版本的 Python 向后移植功能。 docs.python.org/2/library/…
  • 如果,代码在“with”部分失败......在我的情况下,with 部分失败......所以我想显示一条消息......就像我们可以为其他普通断言,例如 self.assertEqual(cm.exception.faultCode, 101001, 'Fault code does not match expected fault code %d' % 101001)
  • @arindamroychowdhury,很抱歉,我已经有一段时间没有编写任何 Python 代码了,所以我不知道你的问题的答案。祝你好运。也许,这里的其他人可以更好地回答您的问题。祝你好运。
  • 我使用的是 Python 2.7。我不得不用 cm.exception.parameter 替换 str(cm.exception)。
  • 这个答案没有错,但stackoverflow.com/a/16282604/6419007 可能要好得多。
【解决方案2】:

您正在寻找assertRaisesRegex,它从 Python 3.2 开始可用。来自文档:

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

或:

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

PS:如果您使用的是 Python 2.7,那么正确的方法名称是 assertRaisesRegexp

【讨论】:

  • 是的,但是如果没有出现预期的错误,您将永远不会看到消息/无法更改默认消息。在循环中测试一些参数时非常不舒服 - 你不知道函数通过哪个参数而没有预期的错误。
  • 在 python 3.6 上,使用 with self.assertRaisesRegexp( RuntimeError, '...regex...' ) 时会显示 DeprecationWarning: Please use assertRaisesRegex instead
  • 这太棒了。简洁,非常强大,比简单地测试一些异常要精确得多。
【解决方案3】:

开箱即用的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 而非unittest.TestCase 派生您的单元测试类。

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

【讨论】:

  • +1 表示“在代码中拼错单词的开发人员也会在测试用例中拼错。”
  • 对我来说,当您在测试时看到 specific 错误正在发生,但由于意外的副作用,测试可以“通过”,这更加令人震惊。例如。您期望的错误没有出现,但在其他地方出现了相同的错误类型,从而满足了测试。测试通过,代码有问题。对您要查找的错误进行子类化的错误也是如此——如果您的测试过于笼统,您最终会发现一些您意想不到的东西。
  • 您应该使用 inst.args[0] 而不是 inst.message 在 Python 2 和 Python 3 上运行此代码
  • 这不是真的,开箱即用的 unittest 确实做到了。
  • Neato 子类化示例。但我很难相信这是必要的或有利的,因为unittest 的“开箱即用”功能非常广泛。
【解决方案4】:

如果你想让错误信息完全匹配:

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

【讨论】:

  • 我必须使用str(error.exception) 而不是error.exception.message 因为error.exception 在我的情况下没有message 属性。
【解决方案5】:

mkelley33 给出了很好的答案,但是这种方法可以被 Codacy 等一些代码分析工具检测为问题。问题是它不知道assertRaises 可以用作上下文管理器,并且它报告并非所有参数都传递给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. 我们测试异常类型。
  2. 我们可以在 Python 2 和 Python 3(我们称之为e.args[0],因为 Py3 中的错误没有 message 属性)。

【讨论】:

  • 这是一个非常优雅的 IMO 解决方案。如果你不喜欢e.args[0],你也可以调用str(e)
猜你喜欢
  • 2021-01-30
  • 2010-11-19
  • 2013-07-09
  • 1970-01-01
  • 2012-10-19
  • 1970-01-01
  • 2020-02-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多