Programing

Python의 unittest로 경고가 발생했는지 테스트하는 방법은 무엇입니까?

lottogame 2021. 1. 11. 07:27
반응형

Python의 unittest로 경고가 발생했는지 테스트하는 방법은 무엇입니까?


Python에 다음 함수가 있으며 함수가 0을 인수로 가져 오면 경고가 발생하는지 unittest로 테스트하고 싶습니다. 나는 이미 assertRaises를 시도했지만 경고를 제기하지 않았기 때문에 작동하지 않습니다.

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

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)

Python 3.2부터는 간단히 assertWarns()메소드 를 사용할 수 있습니다 .

with self.assertWarns(Warning):
    do_something()

catch_warnings 컨텍스트를 캡슐화하기 위해 자신의 assertWarns 함수를 작성할 수 있습니다. 믹스 인을 사용하여 다음과 같은 방식으로 구현했습니다.

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

사용 예 :

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
        )

The test will pass if at least one of the warnings issued by your_function is of type UserWarning.


@ire_and_curses' answer is quite useful and, I think, canonical. Here is another way to do the same thing. This one requires Michael Foord's excellent Mock library.

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

The nifty patch_object lets you mock out the warn method.


One problem with the warnings.catch_warnings approach is that warnings produced in different tests can interact in strange ways through global state kept in __warningregistry__ attributes.

To address this, we should clear the __warningregistry__ attribute of every module before every test that checks warnings.

class MyTest(unittest.TestCase):

  def setUp(self):
    # The __warningregistry__'s need to be in a pristine state for tests
    # to work properly.
    for v in sys.modules.values():
      if getattr(v, '__warningregistry__', None):
        v.__warningregistry__ = {}

  def test_something(self):
    with warnings.catch_warnings(record=True) as w:
      warnings.simplefilter("always", MySpecialWarning)
      ...
      self.assertEqual(len(w), 1)
      self.assertIsInstance(w[0].message, MySpecialWarning)

This is how Python 3's assertWarns() method is implemented.

ReferenceURL : https://stackoverflow.com/questions/3892218/how-to-test-with-pythons-unittest-that-a-warning-has-been-thrown

반응형