2

I'm having a problem trying to patch a static method, no matter what i've tried the original method is still being ran. I'll illustrate an example below.

file A

import B
from unittest.mock import patch
from unittest import mock
import asynctest
import unittest

class Test(asynctest.TestCase):
    @patch('B.OT._func')
    async def test(self, mock_func):
        mock_func.return_value = mock.MagicMock()
        await B.foo()
        mock_func.assert_called_once()
if __name__ == '__main__':
    unittest.main()

file B

from path.C import OT
  
async def foo():
    print('B')
    return OT._func()

path(folder)/file C


class OT:
    @staticmethod
    def _func():
        print('c')
        return True

Problem

I'm having issues mocking the return value for OT._func(). Whatever I try to patch in file A doesn't stop _func() from running in class B. If the test case is ran it prints out 'c' instead of patching the function. My desired outcome is to patch out _func and have it return something else.

Edit: The issue seems to be with the async portion of the test, if the async is removed it works. A workaround for now is using the context manager version (with keyword) to patch stuff.

Daniel
  • 31
  • 4
  • You **must** provide a [mcve]. `from c.TestClass import OT` would throw an ImportError, there **is no OT** in `c`, and `TestClass` is a *class inside `c`*, you can't make it part of the import path, which can only contain *modules/packages* – juanpa.arrivillaga Sep 02 '21 at 17:40
  • Dude, the code *still doesn't even compile*. There is also *no class B*. And again, it *still isn't clear what behavior you are expecting* – juanpa.arrivillaga Sep 02 '21 at 18:24
  • 1
    It looks like you're attempting to patch a function on an object, which means you want to patch the object with `patch.objec()`. See [this](https://stackoverflow.com/questions/18191275/using-pythons-mock-patch-object-to-change-the-return-value-of-a-method-called-w). – Nathaniel Ford Sep 02 '21 at 18:27
  • @NathanielFord Hi, I've tried that as well by using '@patch.object(B.OT, '_func')' instead of '@patch' and it still doesn't work. – Daniel Sep 02 '21 at 18:33
  • @juanpa.arrivillaga I made a few changes to make it more clear. I copy paste the code and it runs. Also there is no class B, it is running a function defined in that file. – Daniel Sep 02 '21 at 18:34
  • @Daniel I think, then, that there is likely a subtle problem with the path you're trying to patch. I am not in a position to replicate your code quite yet, but you might [look at this](https://stackoverflow.com/a/1796247/442945) for an idea on how to display what Python thinks it has, and match it against what you think it has. In particular, since OT has to be patched at the point it's imported to B, making sure that path is correct is necessary. I think you probably know this, but it's where I'd debug next. – Nathaniel Ford Sep 02 '21 at 19:46
  • @NathanielFord Thank you for the article. After looking into it more it seems to be an issue with async and the '@patch' decorator specifically. Don't have a solution yet but I have narrowed it down to that. – Daniel Sep 02 '21 at 22:13

1 Answers1

1

After looking into it more it seems to be an issue with async and the '@patch' decorator specifically (and possible python 3.7). A simple workaround is to using patching alongside the 'with' keyword. This will bypass the patch decorator not working for async functions.

Alternatively, upgrading from python 3.7.2 to 3.8.2 also fixed the issue and allows patch decorators to work with async functions.

Daniel
  • 31
  • 4