Reputation: 105
I am writing python unittests and need to patch a function from a class not explicitly imported at the call site. I encounter an error unless I add the import (unused) to the file the call is from. I would like to know if there is a way around this unused import.
I have created an example with the same issue:
test_1.py
class Test1():
def test_1_func(self, arg):
print(f"This is test_1_func, called with {arg}")
test_2.py
from test_1 import Test1
class Test2():
def __init__(self):
self.test1 = Test1()
test_3.py
from test_2 import Test2
class Test3():
def __init__(self):
self.test2=Test2()
def test_3_func(self):
print("This is test_3_func")
self.test2.test1.test_1_func("ARGUMENT")
tests.py
import unittest
from mock import patch
from test_3 import Test3
class MyTestCase(unittest.TestCase):
@patch('test_3.Test1.test_1_func')
def test_test_3(self, mock_test_1_func):
Test3().test_3_func()
mock_test_1_func.assert_called_with("ARGUMENT")
When test_test_3
is run, I get the error:
ModuleNotFoundError: No module named 'test_3.Test1'; 'test_3' is not a package
However, when I add Test1
as an import in test_3.py
(as below) the test completes without error.
from test_1 import Test1
from test_2 import Test2
class Test3():
def __init__(self):
self.test2=Test2()
def test_3_func(self):
print("This is test_3_func")
self.test2.test1.test_1_func("ARGUMENT")
Upvotes: 1
Views: 2505
Reputation: 11651
You should still be able to use the absolute path:
@patch('test_1.Test1.test_1_func')
I am a little curious as to why it works, I thought the patch had to be at the call site
It's enough that the location it eventually gets read from is swapped out for the mock. Think about the structure of the object graph. Most Python objects have a __dict__
for their attributes (unless this has been specifically turned off), including classes and modules. The test_1
module has a reference to the Test1
class, which has a reference to the test_1_func
function. That last reference Test1.__dict__['test_1_func']
is what gets patched.
The usage path is self.test2.test1.test_1_func
. That last link is test1
(an instance of the Test1
class) to test_1_func
. When an instance object can't find an attribute in its __dict__
, it will look for it in its class's __dict__
, which is where the patch occurred, so it returns the mock.
(I'm simplifying a little bit here, since functions are descriptors, this overrides their attribute access and returns them as bound methods instead, but it still uses the mock.)
Upvotes: 1