Python 模拟 Autospec 与 Spec

Python Mock Autospec vs Spec

试图了解 Spec 和 Autospec 之间的区别。他们似乎差不多。具体来说,如果您查看 mock.patch decorator.

有人可以解释一下什么时候使用哪个吗?

https://docs.python.org/3/library/unittest.mock.html

spec 用作 Mock 对象的模板。用documentation的话来说:

If you use the spec or spec_set arguments then only magic methods that exist in the spec will be created.

这意味着您不能在模拟对象上调用您正在模拟的对象上不存在的方法。 documentation 是这样解释的:

Note If you use the spec keyword argument to create a mock then attempting to set a magic method that isn’t in the spec will raise an AttributeError.

autospec 基本上是 patch 中的 shorthand,用于将您修补的对象传递给正在创建的 MagicMockspec。文档:

If you set autospec=True then the mock with be created with a spec from the object being replaced.

spec 仅适用于指定的模拟实例。特别是,如果模拟的 class a 有一个方法,例如method(),然后在 a 的实例化 mock 中调用该方法将自动生成 return 另一个不受任何规范限制的 mock。这就是 autospec 派上用场的地方,因为它递归地定义了被调用的规范(在当时定义的规范的限制范围内)。

来自Mock Autospeccing Helper documentation

If you use a class or instance as the spec for a mock then you can only access attributes on the mock that exist on the real class:

>>> import urllib2
>>> mock = Mock(spec=urllib2.Request)
>>> mock.assret_called_with
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'

The spec only applies to the mock itself, so we still have the same issue with any methods on the mock:

>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with()

Auto-speccing solves this problem.

我将在 unittest.mock.patch 上对其进行解释,因为它有两个选项 spec,预计将用作模拟规范的对象和 autospec - 类似于规范,除了它将为规范对象的所有“子”模拟提供规范,下面是这些用例的说明:

from unittest.mock import Mock, create_autospec, patch
from unittest import TestCase


class MyClass:
    
    @staticmethod
    def method(foo, bar):
        print(foo)


def some_method(some_class: MyClass):
    arg = 1
    # Would fail because of wrong parameters passed to method.
    return some_class.method(arg)


class TestSomethingTestCase(TestCase):
    def test_something_with_patch_spec(self):
        with patch(f'{__name__}.MyClass', spec=True) as mock:
            # Fails because of signature misuse.
            result = some_method(mock)
        self.assertTrue(result)
        self.assertTrue(mock.method.called)
    
    def test_second_with_patch_autospec(self):
        with patch(f'{__name__}.MyClass', autospec=True) as mock:
            # Fails because of signature misuse.
            result = some_method(mock)
        self.assertTrue(result)
        self.assertTrue(mock.method.called)

带有 autospec 的测试用例将捕获代码中的错误,因为 MyClass.method 签名与 some_method 上预期的签名不对应,这是预期的,而 test_something_with_patch_spec规范使用会给你 误报,所以测试不会履行其职责。

unittest.mock.Mock has only spec option, as an alternative you can use unittest.mock.create_autospec,因此它将创建具有上面示例中描述的自动指定效果的 Mock。