简体   繁体   中英

What is the difference between using the '@patch.object' and 'with patch.object' in Python?

In writing unit tests for my application, I have always been using the @mock.patch and @patch.object decorators. But now, for some unit tests when I use the decorator, I receive an error ' TypeError: staticmethod object is not an iterator '.

But with the same code, if I use mock.patch.object or mock.patch.object , everything works just fine.

For example, in my test class I have this method:

@staticmethod
def my_mock():
   ...do something

When I try the following unit test

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

I receive the error message stated before ' TypeError: staticmethod object is not an iterator '.

But when I try this way

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

then everything works perfectly.

I've read the Python documentation about mock and unit tests, but I couldn't find any explanation for this behavior.

What is the difference between using the decorator pattern and the with pattern? Where I can find more about this?

Just to be more clear, this my code structure:

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

That's why I can't do TestClass.my_mock . If I do, I get a reference error.

You are seeing the effect of Python's descriptor protocol. The difference is not in how you are calling patch , but in the value you are assigning to the side_effect attribute in each case.

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)

If you run this code, you'll see that the print statement inside the class declaration outputs <type 'staticmethod'> , because you have a reference to the method itself.

The other two print statements output <type 'function'> because you don't have a reference to the method; you have a reference to the return value of the method's __get__ method. The two calls are equivalent to

print type(A.__dict__['my_mock'].__get__(A))
print type(A.__dict__['my_mock'].__get__(A()))

See https://docs.python.org/2/howto/descriptor.html for a fuller discussion of how descriptors are used to implement the three types of methods (static, class, and instance).


The actual error comes about because patch expects a callable as the value of the side_effect argument, and failing that, it needs an iterable of return values. A staticmethod object is neither callable nor iterable. (Try it: A.__dict__['my_mock']() .)

To ensure you get the function, you need to access the method through the class.

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

I think you just need to add the class name

class mymodule:
    @staticmethod
    def my_mock():
        ...do something
...

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM