Batu Lab NotesPractical developer guides

Patch the lookup namespace when a dependency was imported directly

By Batu · English technical notes

Also published in our primary archive.

Patch the lookup namespace when a dependency was imported directly

Patch the name the code under test looks up. If a consumer bound provider.fetch earlier—equivalent to from provider import fetch—later replacing provider.fetch does not replace consumer.fetch.

The fixture creates two ModuleType objects without importing application files. consumer.fetch is bound to the provider's original function, and render() calls that consumer attribute. During the first context manager, the provider attribute is visibly patched, but render() still returns real; the installed mock has no calls. That is the failure mode: a patch can be active and still intercept nothing relevant.

The second context manager patches consumer.fetch, the lookup location used by render(). Its return value becomes stub, and the assertion records the call. This contrast is more useful than assuming a patch target from the function's defining module: inspect how the consumer acquired and resolves the dependency. A consumer that instead uses provider.fetch() at call time would require patching the provider attribute.

unittest.mock has been in the standard library since Python 3.3. patch() documentation states the central rule: patch where an object is looked up.

AI assistance disclosure: this article and its synthetic fixture were drafted with AI assistance and should be adapted to the reader's own import structure.

from types import ModuleType
from unittest.mock import Mock, patch

provider = ModuleType("provider")
consumer = ModuleType("consumer")

def fetch():
    return "real"

provider.fetch = fetch
consumer.fetch = provider.fetch  # Equivalent to: from provider import fetch

def render():
    return consumer.fetch()

wrong_target = Mock(return_value="wrong")
with patch.object(provider, "fetch", wrong_target):
    assert provider.fetch is wrong_target
    assert render() == "real"
    assert wrong_target.call_count == 0

right_target = Mock(return_value="stub")
with patch.object(consumer, "fetch", right_target):
    assert render() == "stub"
    right_target.assert_called_once_with()

print("provider patch: real, calls=0")
print("consumer patch: stub, calls=1")
provider patch: real, calls=0
consumer patch: stub, calls=1