在 Python 中使用“@patch.object”和“with patch.object”有什么区别?

Eri*_*ani 7 python unit-testing python-2.7 python-mock python-unittest

在为我的应用程序编写单元测试时,我一直在使用@mock.patch@patch.object装饰器。但是现在,对于使用装饰器时的某些单元测试,我收到错误“ TypeError: staticmethod object is not an iterator ”。

但是使用相同的代码,如果我使用mock.patch.objector mock.patch.object,一切正常。

例如,在我的测试类中,我有这个方法:

@staticmethod
def my_mock():
   ...do something
Run Code Online (Sandbox Code Playgroud)

当我尝试以下单元测试时

@mock.patch('mypackage.mymodule.my_method', side_effect=my_mock)
def test_something(self, my_method_mocked):
    ...test something
Run Code Online (Sandbox Code Playgroud)

我收到了“ TypeError: staticmethod object is not an iterator ”之前所述的错误消息。

但是当我尝试这种方式时

def test_something(self):
    with patch.object(mymodule, "my_method") as mocked_method:
        mocked_method.side_effect = self.my_mock
        ...test something
Run Code Online (Sandbox Code Playgroud)

然后一切正常。

我已经阅读了有关模拟和单元测试的 Python 文档,但找不到对此行为的任何解释。

使用装饰器模式使用模式有什么区别?我在哪里可以找到更多关于这方面的信息?

为了更清楚,这是我的代码结构:

class TestClass(unittest.TestCase):

    @staticmethod
    def my_mock():
    ...mock
        return service

    # doesn't work
    @mock.patch('mypackage.mymodule.my_method', side_effect=my_mock)
    def test_something(self, my_method_mocked):
        ...test something

    # work 
    def test_something(self):
    with patch.object(mymodule, "my_method") as mocked_method:
        mocked_method.side_effect = self.my_mock
        ...test something
Run Code Online (Sandbox Code Playgroud)

这就是我做不到的原因TestClass.my_mock。如果我这样做,我会收到一个参考错误。

che*_*ner 5

您正在看到 Python 的描述符协议的效果。区别不在于您如何调用patch,而在于您side_effect在每种情况下分配给属性的值。

class A(object):
    @staticmethod
    def my_mock():
        pass

    print type(my_mock)    # As in your decorator case

# As in your context manager case
print type(A.my_mock)
print type(A().my_mock)
Run Code Online (Sandbox Code Playgroud)

如果您运行此代码,您将看到print类声明中的语句输出<type 'staticmethod'>,因为您有对方法本身的引用。

其他两个print语句输出<type 'function'>是因为您没有对该方法的引用;您可以引用该方法的__get__方法的返回值。这两个调用相当于

print type(A.__dict__['my_mock'].__get__(A))
print type(A.__dict__['my_mock'].__get__(A()))
Run Code Online (Sandbox Code Playgroud)

请参阅https://docs.python.org/2/howto/descriptor.html以更全面地讨论如何使用描述符来实现三种类型的方法(静态、类和实例)。


实际的错误是因为patch期望一个可调用的作为side_effect参数的值,如果失败,它需要一个可迭代的返回值。甲staticmethod对象既不调用也不迭代。(试试看:A.__dict__['my_mock']()。)

为确保获得该功能,您需要通过该类访问该方法。

class Foo(object):
    @staticmethod
    def my_mock():
        "whatever it does"

@mock.patch('mypackage.mymodule.my_method', side_effect=Foo.my_mock)
def test_something(self, my_method_mocked):
    ...test something
Run Code Online (Sandbox Code Playgroud)