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

时间:2016-03-10 11:50:19

标签: python unit-testing mocking

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

  

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

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

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

3 个答案:

答案 0 :(得分:20)

解释这一点的唯一明确方法是实际引用documentation使用自动显示的缺点以及为什么使用它时应该小心:

  

然而,这并非没有警告和限制,这就是原因   不是默认行为。为了知道属性是什么   在spec对象上可用,autospec必须内省(访问   属性)规范。当您遍历模拟a上的属性时   相应的遍历原始对象正在发生   引擎盖。如果您的任何推出对象具有属性或描述符   这可能会触发代码执行,然后您可能无法使用   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'

正如您所看到的,在创建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()

所以,我只是说我的测试用例我想确保some_external_thing()方法不会影响我的unittest的行为,所以我只是将我的实例属性分配给每个{{ 1}}。

答案 1 :(得分:0)

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

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

因此,这是默认情况下启用的有问题的功能, 仅选择加入。

答案 2 :(得分:0)

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

取决于对象的复杂程度,使用autospec可能会大大降低测试速度。我在修补Django模型时发现了这一点。