8

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.

Erick Gallani
  • 759
  • 8
  • 23

2 Answers2

6

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
chepner
  • 497,756
  • 71
  • 530
  • 681
  • But I can only reference method like this Foo.my_mock when they are `@classmethod`. With `@staticmethod` doesn't work. – Erick Gallani Aug 10 '16 at 21:17
  • `Foo.my_mock` is *exactly* how you need to access a static method. The only difference between a static method and a class method is that the static method doesn't get a reference to the class as an argument. – chepner Aug 10 '16 at 21:22
  • If I try use like you said `Foo.my_mock` with `my_mock` as a static method, I got an error saying that I can't do that. But with I declare `my_mock` as a class method, than I can use it. – Erick Gallani Aug 12 '16 at 13:04
  • There is no such message that says "[you] can't do that". Be precise. – chepner Aug 12 '16 at 13:15
  • My mock method 'my_mock' is inside of my test class. So if I try to use `side_effect=TestClass.my_mock` in a test method that is inside the same class of my_mock I get a reference error. – Erick Gallani Aug 22 '16 at 13:21
  • If the patch occurs *during* class creation, then you can refer to it as `my_mock`. – chepner Aug 22 '16 at 14:05
0

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
Lars Grundei
  • 101
  • 5