簡體   English   中英

使用unittest.mock.patch時,為什么默認情況下autospec不為True?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM