使用unittest.mock.patch时,为什么autospec默认不是True?
问题描述
当您使用 mock 修补函数时,您可以选择将 autospec 指定为 True:
When you patch a function using mock, you have the option to specify autospec as True:
如果您设置 autospec=True ,则使用规范创建模拟从被替换的对象.模拟的所有属性也将具有对象的相应属性的规范更换.被模拟的方法和函数将有它们的参数检查并在错误调用它们时引发 TypeError签名.
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)
我想知道为什么这不是默认行为?当然,我们几乎总是希望捕捉传递给我们修补的任何函数的错误参数?
I'm wondering why this isn't the default behaviour? Surely we would almost always want to catch passing incorrect parameters to any function we patch?
解决方案
解释这一点的唯一明确方法是实际引用 documentation 关于使用自动规范的缺点以及为什么在使用时应该小心:
The only clear way to explain this, is to actually quote the documentation on the downside of using auto-speccing and why you should be careful when using it:
然而,这并非没有警告和限制,这就是它的原因不是默认行为.为了知道什么是属性在 spec 对象上可用,autospec 必须自省(访问属性)规范.当您遍历模拟上的属性时原始对象的相应遍历发生在兜帽.如果您指定的任何对象具有属性或描述符可以触发代码执行,那么您可能无法使用自动规格.另一方面,设计你的对象要好得多这样自省是安全的 [4].
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].
一个更严重的问题是实例属性很常见在 init 方法中创建并且不存在于类中全部.autospec 不知道任何动态创建的属性和将 api 限制为可见属性.
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 限制为可见属性
I think the key takeaway here is to note this line: autospec can’t know about any dynamically created attributes and restricts the api to visible attributes
因此,为了更清楚地说明自动检测中断的示例,从文档中获取的这个示例显示了这一点:
So, to help being more explicit with an example of where autospeccing breaks, this example taken from the documentation shows this:
>>> 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
对象时会创建属性 a
.
As you can see, auto-speccing has no idea that there is an attribute a
being created when creating your Something
object.
为您的实例属性赋值并没有错.
There is nothing wrong with assigning a value to your instance attribute.
观察下面的函数示例:
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()
方法不会影响我的单元测试的行为,所以我只是分配我的实例属性每个 mock_some_external_thing.return_value = "there be dragons"
的模拟.
So, I'm just saying for my test case I want to make sure that the some_external_thing()
method does not affect the behaviour of my unittest, so I'm just assigning my instance attribute the mock per mock_some_external_thing.return_value = "there be dragons"
.
相关文章