I am aware of the importance of path to mock as illustrated here, but consider this Django scenario
models.py
class Proxmox(Model):
@property
def api(self, ...):
....
tasks.py
def run_task(...):
....
views.py
from models import Proxmox
from tasks import run_task
class APIView(...):
def get(request):
Proxmox.objects.get(...).api.do_something()
run_task()
tests.py
class MyTestCase(...):
@mock.patch('tasks.run_task') <---- this is not patched as already imported in view
#@mock.patch('views.run_task'). <---- this patches fine
@mock.patch('models.Proxmox.api') <---- but why this works fine?
def test_one(self, mock_api, mock_run_task):
client.get(....)
...
Both Proxmox
and run_task
are imported into views.py
so why patching models.Proxmox.api
still got patched?
In short, it's because "api" is looked up in the namespace of a type, rather than in the namespace of a module.
What matters with patching is how/where a name is looked up, not where it is defined. In this case the name "api" is looked up on the type Proxmox
, regardless of which module namespace the name Proxmox
is found in.
Here mock patches the class namespace of Proxmox
, replacing the property descriptor.
It works because, no matter where where the type Proxmox
is imported from, accessing Proxmox.api
will always be an attribute access on the Proxmox
class. All module namespaces will see that same patch, because they all point to the same identical class type.