[英]Why I cannot effectively mock function with unittest.mock.patch?
[英]When using unittest.mock.patch, why is autospec not True by default?
使用mock修補函數時,可以選擇將autospec指定為True:
如果設置autospec = True,則使用要替換的對象的規范創建模擬。 mock的所有屬性也將具有要替換的對象的相應屬性的規范。 被模擬的方法和函數將檢查其參數,如果使用錯誤的簽名調用它們將引發TypeError。
( http://www.voidspace.org.uk/python/mock/patch.html )
我想知道為什么這不是默認行為? 當然,我們幾乎總是希望將錯誤的參數傳遞給我們修補的任何函數?
唯一明確的解釋方法是,實際引用文檔說明使用自動查詢的缺點以及使用它時為什么要小心:
然而,這並非沒有警告和限制,這就是為什么它不是默認行為。 為了知道spec對象上可用的屬性,autospec必須內省(訪問屬性)規范。 當您在模擬上遍歷屬性時,原始對象的相應遍歷發生在引擎蓋下。 如果您的任何特定對象具有可以觸發代碼執行的屬性或描述符,那么您可能無法使用自動規范。 另一方面,設計對象要好得多,內省是安全的[4]。
更嚴重的問題是,例如,在init方法中創建屬性並且根本不存在於類中是常見的。 autospec無法了解任何動態創建的屬性,並將api限制為可見屬性。
我認為這里的關鍵點是注意這一行: autospec無法知道任何動態創建的屬性並將api限制為可見屬性
因此,為了幫助更明確地說明自動指定中斷的示例,從文檔中獲取的此示例顯示了以下內容:
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
正如你所看到的,自動speccing不知道,有一個屬性, a
創建時所創建Something
對象。
為實例屬性賦值沒有任何問題。
請注意以下功能示例:
import unittest
from mock import patch
def some_external_thing():
pass
def something(x):
return x
class MyRealClass:
def __init__(self):
self.a = some_external_thing()
def test_thing(self):
return something(self.a)
class MyTest(unittest.TestCase):
def setUp(self):
self.my_obj = MyRealClass()
@patch('__main__.some_external_thing')
@patch('__main__.something')
def test_my_things(self, mock_something, mock_some_external_thing):
mock_some_external_thing.return_value = "there be dragons"
self.my_obj.a = mock_some_external_thing.return_value
self.my_obj.test_thing()
mock_something.assert_called_once_with("there be dragons")
if __name__ == '__main__':
unittest.main()
所以,我只是說我的測試用例我想確保some_external_thing()
方法不會影響我的unittest的行為,所以我只是將我的實例屬性分配給mock每個mock_some_external_thing.return_value = "there be dragons"
。
自動指定本身的操作可以執行代碼,例如通過調用描述符。
>>> class A:
... @property
... def foo(self):
... print("rm -rf /")
...
>>> a = A()
>>> with mock.patch("__main__.a", autospec=False) as m:
... pass
...
>>> with mock.patch("__main__.a", autospec=True) as m:
... pass
...
rm -rf /
因此,這是一個有問題的功能,默認啟用並且僅選擇加入。
許多年后回答我自己的問題 - 另一個原因是速度。
根據您的對象的復雜程度,使用autospec可能會顯着降低您的測試速度。 我在修補Django模型時發現了這一點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.