Why python mock patch doesn't work?
Categories:
Demystifying Python's mock.patch
: Why Your Mocks Aren't Working

Uncover the common pitfalls and subtle nuances of Python's unittest.mock.patch
decorator and context manager, and learn how to effectively isolate your code for robust unit testing.
Python's unittest.mock
library, particularly mock.patch
, is an incredibly powerful tool for unit testing. It allows you to temporarily replace objects in your system under test with mock objects, isolating the code you're testing from its dependencies. However, many developers encounter frustrating scenarios where their mocks simply don't seem to work. This article dives into the core reasons behind these failures, focusing on common misunderstandings and subtle details that can make or break your patching efforts.
The Golden Rule of Patching: Where Objects Are Looked Up
The most frequent reason mock.patch
fails is a misunderstanding of where the object being patched is looked up. You don't patch where an object is defined; you patch where it's used. This is often referred to as 'patching where the object lives' or 'patching where it's imported into the module under test'.
Consider a scenario where module_a.py
defines a function my_function
, and module_b.py
imports and uses my_function
. If you're testing a function in module_b
that calls my_function
, you must patch module_b.my_function
, not module_a.my_function
.
flowchart TD A["Object Defined (e.g., module_a.py)"] B["Object Imported/Used (e.g., module_b.py)"] C["Test File (e.g., test_module_b.py)"] D["Correct Patch Target"] E["Incorrect Patch Target"] A -- "Defines 'my_function'" --> B B -- "Imports 'my_function'" --> A C -- "Tests code in B" --> B C -- "Patches 'module_b.my_function'" --> D D -- "Mock applied successfully" --> B C -- "Attempts to patch 'module_a.my_function'" --> E E -- "Mock not applied to B's reference" --> B E -- "Original 'my_function' still called" --> B
Illustrating the 'patch where it's used' principle.
# module_a.py
def external_service_call():
print("Calling actual external service...")
return "Real Data"
# module_b.py
from module_a import external_service_call
def process_data():
data = external_service_call()
return f"Processed: {data}"
# test_module_b.py
import unittest
from unittest.mock import patch
from module_b import process_data
class TestProcessData(unittest.TestCase):
# Correct way to patch: patch where it's used (in module_b)
@patch('module_b.external_service_call')
def test_process_data_correct_patch(self, mock_service_call):
mock_service_call.return_value = "Mocked Data"
result = process_data()
self.assertEqual(result, "Processed: Mocked Data")
mock_service_call.assert_called_once()
# Incorrect way to patch: patching where it's defined (in module_a)
@patch('module_a.external_service_call')
def test_process_data_incorrect_patch(self, mock_service_call):
mock_service_call.return_value = "Mocked Data"
result = process_data() # This will still call the real external_service_call
# The assertion below would fail if the real function prints 'Calling actual external service...'
# self.assertEqual(result, "Processed: Mocked Data")
# mock_service_call.assert_called_once() # This would also fail
print(f"Incorrect patch result: {result}")
Demonstrating correct vs. incorrect patching targets.
from some_module import some_object
in the module you are testing, you should patch some_module.some_object
in your test, not original_definition_module.some_object
.Patching Classes and Instances
Patching classes and their instances requires careful consideration. When you patch a class, you are replacing the class itself. Any new instances created after the patch will be instances of your mock class. However, existing instances created before the patch will retain their original class type and methods.
If you need to mock a method on an instance that already exists or is passed into your function, you'll need to patch that specific instance's method, or ensure the instance is created within the scope of your class patch.
# my_library.py
class MyDependency:
def __init__(self, value):
self.value = value
def get_data(self):
print(f"Getting real data: {self.value}")
return f"Real Data for {self.value}"
# my_app.py
from my_library import MyDependency
def process_with_dependency(dep_value):
dep = MyDependency(dep_value)
return dep.get_data()
def process_existing_dependency(existing_dep):
return existing_dep.get_data()
# test_my_app.py
import unittest
from unittest.mock import patch, MagicMock
from my_app import process_with_dependency, process_existing_dependency
from my_library import MyDependency
class TestMyApp(unittest.TestCase):
@patch('my_app.MyDependency')
def test_process_with_dependency_class_patch(self, MockMyDependency):
# Configure the mock instance that will be returned when MyDependency() is called
mock_instance = MagicMock()
mock_instance.get_data.return_value = "Mocked Data from New Instance"
MockMyDependency.return_value = mock_instance
result = process_with_dependency("test")
self.assertEqual(result, "Mocked Data from New Instance")
MockMyDependency.assert_called_once_with("test")
mock_instance.get_data.assert_called_once()
def test_process_existing_dependency_instance_patch(self):
# Create a real instance first
real_dep = MyDependency("original")
# Patch the method directly on the *instance*
with patch.object(real_dep, 'get_data', return_value="Mocked Data from Existing Instance") as mock_get_data:
result = process_existing_dependency(real_dep)
self.assertEqual(result, "Mocked Data from Existing Instance")
mock_get_data.assert_called_once()
# Verify the real method is called outside the patch context
self.assertEqual(real_dep.get_data(), "Real Data for original")
Patching a class for new instances vs. patching a method on an existing instance.
Common Pitfalls and Solutions
Beyond the 'where to patch' rule, several other issues can lead to mock.patch
not behaving as expected.
open
or time.sleep
.1. Incorrect Import Order or Scope
If you import a module before applying a patch, the module will already have a reference to the unpatched object. Ensure your patch
decorator or context manager is applied before the code that uses the patched object is executed. For decorators, this means placing them above the test method or class definition. For context managers, ensure they wrap the relevant code block.
2. Patching a Global Variable
When patching global variables, remember to patch them in the module where they are used, not necessarily where they are defined. If module_b
imports GLOBAL_VAR
from module_a
, you patch module_b.GLOBAL_VAR
.
3. Using patch.object
vs. patch
patch.object(target, attribute)
is used to patch an attribute on a specific object. This is useful for mocking methods on instances or attributes on specific class objects. patch(target_string)
is more general and patches a named object within a module. Choose the right tool for the job.
4. Forgetting autospec=True
or spec=True
Using autospec=True
(or spec=True
for more manual control) is highly recommended. It creates a mock that mimics the signature of the original object, raising AttributeError
if you try to access non-existent attributes or call methods with incorrect arguments. This helps catch typos and ensures your tests are more robust.
import unittest
from unittest.mock import patch, MagicMock
# Example of a module with a global variable
# config_module.py
API_KEY = "real_api_key"
# app_module.py
import config_module
def get_api_data():
print(f"Using API Key: {config_module.API_KEY}")
return f"Data with {config_module.API_KEY}"
# test_app_module.py
class TestAppModule(unittest.TestCase):
@patch('app_module.config_module.API_KEY', 'mocked_api_key')
def test_get_api_data_global_patch(self):
result = get_api_data()
self.assertEqual(result, "Data with mocked_api_key")
# Example of autospec
def test_autospec_example(self):
class MyService:
def fetch_user(self, user_id):
pass
with patch('__main__.MyService', autospec=True) as MockService:
instance = MockService()
instance.fetch_user(123) # This works
# instance.fetch_user(123, 'extra_arg') # This would raise a TypeError with autospec
# instance.non_existent_method() # This would raise an AttributeError with autospec
instance.fetch_user.assert_called_once_with(123)
Patching a global variable and demonstrating autospec
.
Mastering mock.patch
requires a solid understanding of Python's import mechanism and object lookup rules. By consistently applying the 'patch where it's used' principle and being mindful of object lifetimes, you can effectively isolate your code and write more reliable unit tests.