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.
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.
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.
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 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 .
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With