2010-10-08 49 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 2016-09-16 15:12:59

回答

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。漂亮而有用。 – 2010-10-08 16:12:51

+0

注意這不是線程安全的,因爲它修改了一個'全局狀態' - 如果你在一個測試套件中使用它併發出其他警告,這些也會顯示在'catch_warnings'中,這可能會導致錯誤的否定。 – nlsdfnbch 2017-12-17 09:58:16

4

@ire_and_curses' answer是非常有用的,而且我認爲,規範。這是另一種做同樣事情的方法。這一個需要Michael Foord的優秀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 2013-08-21 12:35:11

+0

+1 - 這個解決方案比接受的答案要好,因爲它不使用'warnings'庫的全局狀態 - 這可能會導致錯誤的否定。 – nlsdfnbch 2017-12-17 10:00:46

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 2015-05-20 17:45:10

+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 raised 。有人可以發表一個例子嗎? – alemol 2015-05-20 17:52:03

+0

也許這是壞的文件...也許它工作,如果你提出UserWarning,但你不應該提出警告,你應該警告。警告「他們,因此需要不同的捕獲 – Anentropic 2015-12-02 17:02:31

11

從Python 3.2開始,您可以簡單地使用assertWarns()方法。

with self.assertWarns(Warning): 
    do_something() 
相關問題