2010-10-08 2 views
32

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

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

Спасибо, Томас

+0

Что касается работы вокруг * ... если предупреждение уже поднимался из-за правило один раз/по умолчанию, не то независимо от того, какие фильтры устанавливаются предупреждения не будут видны снова, если в реестре предупреждений не связанных с предупреждением был очищен. * ([docs] (https://docs.python.org/3/library/warnings.html#testing-warnings)) см. [эту статью] (https://blog.ionelmc.ro/2013/06/26/testing-python-warnings /) о уровне модуля '__warningregistry__' (упоминаются в документах реестра). – saaj

ответ

30

Вы можете использовать менеджер catch_warnings контекста. По сути, это позволяет вам высмеивать обработчик предупреждений, чтобы вы могли проверить информацию об этом предупреждении. См. official docs для более полного объяснения и примерного тестового кода.

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

+1. Прекрасный и полезный. –

+0

Обратите внимание, что это НЕ является потокобезопасным, потому что оно изменяет «глобальное состояние» - если вы используете это в тестовом наборе и вызывается другие предупреждения, они также будут отображаться в 'catch_warnings', что может вызвать ложные негативы. – nlsdfnbch

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.

+0

Хорошее решение .. – pelson

+0

+1 - Это решение лучше, чем принятый ответ, поскольку он не использует глобальное состояние библиотеки 'warnings', что может вызвать ложные негативы. – nlsdfnbch

17

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

+0

Это отвечает worksassertRaisesRegexp (UserWarning, # 'Входной файл не является pdf: Использование только tika', # metac.apply, # self.notpdf) – alemol

+0

Это ответы работает. Однако [документация] (https://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaisesRegexp) говорит, что «также можно проверить, что исключения и предупреждения возникают с помощью следующих методов: assertRaises (исключение, вызываемый, * args, ** kwds), assertRaises (исключение) " Итак, я попробовал ' assertRaisesRegexp (UserWarning, 'мое военное сообщение', function_which_issues_a_warning, args) ', но мой тест не прошел: AssertionError: UserWarning not raise , Может ли кто-нибудь отправить пример? – alemol

+0

возможно, это плохой документ ... возможно, он работает, если вы поднимете UserWarning', но вы не должны поднимать предупреждения, вы должны «предупреждать».warn() 'их, следовательно, необходимость в другом улове – Anentropic

11

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

with self.assertWarns(Warning): 
    do_something() 
Смежные вопросы