Подтвердить что ты не робот

Как проверить с Python unittest, что предупреждение было брошено?

У меня есть следующая функция в Python, и я хочу проверить с unittest, что если функция получает 0 в качестве аргумента, она выдает предупреждение. Я уже пробовал assertRaises, но так как я не выдвигаю предупреждение, это не работает.

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

Ответ 1

Вы можете использовать контекстный менеджер 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)

Ответ 2

Начиная с Python 3.2, вы можете просто использовать метод assertWarns().

with self.assertWarns(Warning):
    do_something()

Ответ 3

Вы можете написать свою собственную функцию 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))

Пример использования:

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
        )

Тест пройдет, если хотя бы одно из предупреждений, выданных your_function, имеет тип UserWarning.

Ответ 4

@ire_and_curses answer весьма полезен и, я думаю, канонический. Вот еще один способ сделать то же самое. Для этого требуется Майкл Фоорд 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()

Отличный patch_object позволяет вам изловить метод warn.

Ответ 5

Одна проблема с подходом warnings.catch_warnings заключается в том, что предупреждения, создаваемые в разных тестах, могут взаимодействовать странным образом через глобальное состояние, сохраняемое в атрибутах __warningregistry__.

Для решения этой проблемы мы должны очистить атрибут __warningregistry__ каждого модуля перед каждым тестом, проверяющим предупреждения.

class MyTest(unittest.TestCase):

  def setUp(self):
    # The __warningregistry__ 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)

Именно так реализован метод Python 3 assertWarns().