如何使用Python的unittest测试是否已经抛出警告?

Tom*_*tny 55 python warnings unit-testing exception-handling

我在Python中有一个以下函数,我想用unittest测试如果函数得到0作为参数,它会抛出一个警告.我已经尝试过assertRaises,但由于我没有提出警告,这不起作用.

def isZero(i):
    if i != 0:
        print "OK"
    else:
        warning = Warning("the input is 0!") 
        print warning
    return i
Run Code Online (Sandbox Code Playgroud)

ire*_*ses 50

您可以使用catch_warnings上下文管理器.实质上,这允许您模拟警告处理程序,以便您可以验证警告的详细信息.有关更全面的解释和示例测试代码,请参阅官方文档.

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings(record=True) as w:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")
    # Trigger a warning.
    fxn()
    # Verify some things
    assert len(w) == 1
    assert issubclass(w[-1].category, DeprecationWarning)
    assert "deprecated" in str(w[-1].message)
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这不是线程安全的,因为它会修改“全局状态”-如果在测试套件中使用此状态并且发出其他警告,则这些警告也会显示在“ catch_warnings”中,这可能会导致误报。 (3认同)

Mel*_*ius 47

从Python 3.2开始,您可以简单地使用assertWarns()方法.

with self.assertWarns(Warning):
    do_something()
Run Code Online (Sandbox Code Playgroud)

  • ...或运行“self.assertWarns()”但在测试上粘贴“@unittest.expectedFailure”。 (2认同)

Ana*_*kov 18

您可以编写自己的assertWarns函数来填充catch_warnings上下文.我刚刚通过以下方式实现了它:mixin:

class WarningTestMixin(object):
    'A test which checks if the specified warning was raised'

    def assertWarns(self, warning, callable, *args, **kwds):
        with warnings.catch_warnings(record=True) as warning_list:
            warnings.simplefilter('always')

            result = callable(*args, **kwds)

            self.assertTrue(any(item.category == warning for item in warning_list))
Run Code Online (Sandbox Code Playgroud)

一个用法示例:

class SomeTest(WarningTestMixin, TestCase):
    'Your testcase'

    def test_something(self):
        self.assertWarns(
            UserWarning,
            your_function_which_issues_a_warning,
            5, 10, 'john', # args
            foo='bar'      # kwargs
        )
Run Code Online (Sandbox Code Playgroud)

如果发出的警告中至少有一个your_function是UserWarning类型,则测试将通过.


Man*_*dan 6

@ire_and_curses的答案非常有用,我认为,这是规范的.这是另一种做同样事情的方法.这个需要Michael Foord的优秀Mock图书馆.

import unittest, warnings
from mock import patch_object

def isZero( i):
   if i != 0:
     print "OK"
   else:
     warnings.warn( "the input is 0!")
   return i

class Foo(unittest.TestCase):
    @patch_object(warnings, 'warn')
    def test_is_zero_raises_warning(self, mock_warn):
        isZero(0)
        self.assertTrue(mock_warn.called)

if __name__ == '__main__':
    unittest.main()
Run Code Online (Sandbox Code Playgroud)

nifty patch_object让你模拟出这个warn方法.