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

2024-06-07 07:03:18 发布

您现在位置:Python中文网/ 问答频道 /正文

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

If you set autospec=True then the mock with be created with a spec from the object being replaced. All attributes of the mock will also have the spec of the corresponding attribute of the object being replaced. Methods and functions being mocked will have their arguments checked and will raise a TypeError if they are called with the wrong signature.

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

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


Tags: andofthe函数trueifobjecthave
3条回答

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

>>> 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 /

因此,在默认情况下启用和 仅限选择加入。

解释这一点的唯一明确方法是,实际引用使用auto speccing的下行上的documentation,以及为什么在使用时要小心:

This isn’t without caveats and limitations however, which is why it is not the default behaviour. In order to know what attributes are available on the spec object, autospec has to introspect (access attributes) the spec. As you traverse attributes on the mock a corresponding traversal of the original object is happening under the hood. If any of your specced objects have properties or descriptors that can trigger code execution then you may not be able to use autospec. On the other hand it is much better to design your objects so that introspection is safe [4].

A more serious problem is that it is common for instance attributes to be created in the init method and not to exist on the class at all. autospec can’t know about any dynamically created attributes and restricts the api to visible attributes.

我认为这里的关键是要注意这一行: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'

如您所见,在创建Something对象时,auto speccing并不知道正在创建属性a

为实例属性分配值没有错。

请注意以下功能示例:

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 permock_some_external_thing.return_value = "there be dragons"

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

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

相关问题 更多 >