Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 780
Erick Gallani Avatar asked Aug 10 '16 19:08

Erick Gallani


People also ask

What is the difference between mock and MagicMock?

So what is the difference between them? MagicMock is a subclass of Mock . It contains all magic methods pre-created and ready to use (e.g. __str__ , __len__ , etc.). Therefore, you should use MagicMock when you need magic methods, and Mock if you don't need them.

What is the difference between mock and patch?

Mock is a type, and patch is a context. So you are going to pass or receive Mock instances as parameters, and apply patch contexts to blocks of code. (Lowercase 'mock' is just the name of the package.) Tests and test classes are often decorated with calls to patch.

What does patch object do?

patch() unittest. mock provides a powerful mechanism for mocking objects, called patch() , which looks up an object in a given module and replaces that object with a Mock . Usually, you use patch() as a decorator or a context manager to provide a scope in which you will mock the target object.

How do I use a patch mock in Python?

How do we mock in Python? Mocking in Python is done by using patch to hijack an API function or object creation call. When patch intercepts a call, it returns a MagicMock object by default. By setting properties on the MagicMock object, you can mock the API call to return any value you want or raise an Exception .


1 Answers

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
like image 92
chepner Avatar answered Oct 18 '22 17:10

chepner