Nathan Gilbert

Mocking Where It’s Imported (Not Defined)

If you’ve ever written tests in Python using pytest and unittest.mock, you’ve probably seen this confusing bit of advice:

“You have to mock where the function is imported, not where it’s defined.”

At first glance, this seems counterintuitive — why should you patch the same function name in multiple places when it’s clearly defined in one module? The answer has everything to do with how Python imports work.

The Import Mechanism

When you write this:

# foo.py
def get_data():
    return "real data"

# And in another module there is this
# bar.py
from foo import get_data

def process():
    return f"processed {get_data()}"

Python copies a reference to foo.get_data into the namespace of bar.

So bar.get_data is not dynamically looked up each time — it’s bound once when bar is imported.

That means if you want to replace get_data() in your tests, mocking foo.get_data won’t help, because bar already has its own local reference to the original function.

The Wrong Way

This is a common mistake:

# test_bar.py
from unittest.mock import patch
import bar

@patch("foo.get_data", return_value="fake data")
def test_process(mock_get):
    result = bar.process()
    assert result == "processed fake data"

This test fails, because bar still calls the real get_data() as it imported from foo before you patched it. Your patch changed foo.get_data, not bar.get_data.

The Right Way

Instead, you need to patch the reference that bar uses:

@patch("bar.get_data", return_value="fake data")
def test_process(mock_get):
    result = bar.process()
    assert result == "processed fake data"

Now it works! The unittest.mock.patch swapped out the object that bar actually calls.

In a pytest fixture

import pytest
from unittest.mock import patch

@pytest.fixture
def mock_get_data():
    with patch("bar.get_data", return_value="mocked") as mock:
        yield mock

def test_process(mock_get_data):
    from bar import process
    assert process() == "processed mocked"

Why This Happens

import statements in Python work by copying a reference from the source module into the importing module’s namespace. Once that reference is bound, the importer doesn’t look it up again. This is different from JavaScript’s live bindings, where imports always reflect the current value of the export. So when you patch something, you’re not changing a global function everywhere — you’re just swapping out one module’s reference to it.

Mocking in Python is about replacing references, not rewriting definitions. Always patch in the namespace where the code under test looks up the function, not where it was first written. That mental model will save you from countless hours of “why isn’t my mock working?” frustration — and make your tests more predictable.

Quick Rules of Thumb

  1. Patch where the function is looked up. Think in terms of “who is calling it,” not “where it lives.”
  2. Use fully qualified paths. Always patch using the dotted path as it’s imported in the module under test (e.g., bar.get_data).
  3. Patch as close to the call site as possible. If bar.process calls it, patch bar.get_data. If baz imports bar and calls bar.process(), patch in baz if that’s where the call happens.