使用unittest.mock.patch时,为什么默认情况下autospec不为True?

sed*_*nym 27 python unit-testing mocking

使用mock修补函数时,可以选择将autospec指定为True:

如果设置autospec = True,则使用要替换的对象的规范创建模拟.mock的所有属性也将具有要替换的对象的相应属性的规范.被模拟的方法和函数将检查其参数,如果使用错误的签名调用它们将引发TypeError.

(http://www.voidspace.org.uk/python/mock/patch.html)

我想知道为什么这不是默认行为?当然,我们几乎总是希望将错误的参数传递给我们修补的任何函数?

idj*_*jaw 25

唯一明确的解释方法是,实际引用文档说明使用自动查询的缺点以及使用它时为什么要小心:

然而,这并非没有警告和限制,这就是为什么它不是默认行为.为了知道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'
Run Code Online (Sandbox Code Playgroud)

如您所见,自动显示不知道a在创建Something对象时是否创建了属性.

通常情况下,对于我自己,我只是模拟补丁并且不要使用autospec,因为行为通常符合我的期望.

为实例属性赋值没有任何问题.

请注意以下功能示例:

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()
Run Code Online (Sandbox Code Playgroud)

所以,我只是说我的测试用例,我想确保该some_external_thing()方法不会影响我的unittest的行为,所以我只是将我的实例属性分配给mock mock_some_external_thing.return_value = "there be dragons".


sed*_*nym 10

多年后回答我自己的问题 - 另一个原因是速度。

根据对象的复杂程度,使用 autospec 可能会显着降低测试速度。我特别在修补 Django 模型时发现了这一点。


wim*_*wim 6

自动指定的操作本身可以执行代码,例如通过调用描述符。

>>> 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 /
Run Code Online (Sandbox Code Playgroud)

因此,这是一个默认启用的有问题的功能,并且只能选择加入。