Python tutorials > Testing > Mocking > How to patch objects/functions?
How to patch objects/functions?
Patching is a powerful technique in Python's testing framework, particularly when using the unittest.mock
module. It allows you to replace objects or functions in your code with mock objects during tests. This isolates the code under test and enables you to control the behavior of dependencies, making your tests more predictable and reliable. This tutorial explores how to use unittest.mock.patch
effectively to mock objects and functions in your Python tests.
Basic Patching Example
This example demonstrates a basic use case of Important: Note that unittest.mock.patch
. We have a function get_data_from_api
that, in a real-world scenario, would retrieve data from an external API. We also have process_data
which uses this data. In our test, we use @patch('__main__.get_data_from_api')
to replace the actual get_data_from_api
function with a mock object. The mock object is then passed as an argument to the test method (test_process_data_with_mock
). We can then configure the mock object's return value using mock_get_data_from_api.return_value = "Mocked API Data"
. This allows us to control what the process_data
function receives as input, isolating it from the external API dependency. Finally, we assert that the result of process_data
is what we expect, given the mocked data.'__main__.get_data_from_api'
is the fully qualified name of the function within the module where the test is run. If your functions are defined in a separate module, you will need to adjust the patch target accordingly (e.g., 'my_module.get_data_from_api'
).
import unittest
from unittest.mock import patch
# Suppose this is in your module 'my_module.py'
def get_data_from_api():
# Normally, this would call an external API
return "Real API Data"
def process_data():
data = get_data_from_api()
return f"Processed: {data}"
class TestProcessData(unittest.TestCase):
@patch('__main__.get_data_from_api') # Replace '__main__.get_data_from_api'
def test_process_data_with_mock(self, mock_get_data_from_api):
# Configure the mock object's return value
mock_get_data_from_api.return_value = "Mocked API Data"
# Call the function that uses the patched function
result = process_data()
# Assert that the result is what you expect given the mocked return value
self.assertEqual(result, "Processed: Mocked API Data")
# Optionally, assert that the mock was called
mock_get_data_from_api.assert_called_once()
if __name__ == '__main__':
unittest.main()
Concepts Behind the Snippet
Several key concepts are demonstrated in the previous snippet:
unittest.mock.patch
.patch
needs to be the location where the object is looked up, not where it's defined. This is a common source of confusion.
Real-Life Use Case
Imagine you're testing a function that interacts with a database. Directly interacting with the database during testing can be slow, unreliable (due to network issues, database availability), and can modify your data. Patching the database connection object (or a function that executes queries) allows you to simulate database interactions without actually touching the database. Another common use case is when dealing with external APIs, as shown in the example above. You don't want your tests to depend on the availability or performance of external services. Patching the function that calls the API allows you to simulate different API responses, including error scenarios.
Patching Attributes of Objects
In this example, we use patch.object
to patch the value
attribute of the MyClass
class. We specify the class (MyClass
), the attribute to patch ('value'
), and the new value to assign to the attribute (new=20
). During the test, the value
attribute of MyClass
is temporarily replaced with 20. This allows us to control the behavior of the MyClass
instance and test the use_my_class
function in isolation.
import unittest
from unittest.mock import patch
class MyClass:
def __init__(self):
self.value = 10
def get_value(self):
return self.value
def use_my_class(obj):
return obj.get_value() + 5
class TestUseMyClass(unittest.TestCase):
@patch.object(MyClass, 'value', new=20) # Patch the 'value' attribute of MyClass
def test_use_my_class_with_patched_attribute(self):
obj = MyClass()
result = use_my_class(obj)
self.assertEqual(result, 25)
if __name__ == '__main__':
unittest.main()
Patching with with
Statement
The This approach ensures that patches are always reverted, even if exceptions occur within the with
statement provides a convenient way to apply and automatically revert patches. In this example, we use patch.dict
to patch the os.environ
dictionary. Within the with
block, the MY_VARIABLE
environment variable is set to 'patched_value'
. After the with
block exits, the os.environ
dictionary is automatically restored to its original state.with
block, preventing unintended side effects on other tests or code execution.
import unittest
from unittest.mock import patch
def my_function():
import os
return os.environ.get('MY_VARIABLE', 'default_value')
class TestMyFunction(unittest.TestCase):
def test_my_function_with_patched_env_variable(self):
with patch.dict('os.environ', {'MY_VARIABLE': 'patched_value'}):
result = my_function()
self.assertEqual(result, 'patched_value')
# After the 'with' block, os.environ is restored to its original state
# result = my_function() # This would now return 'default_value'
if __name__ == '__main__':
unittest.main()
Best Practices
with
statement or the function decorator approach with patch.stopall()
(if you must globally patch) to ensure patches are reverted after the test is complete.mock.assert_called_once()
, mock.assert_called_with()
, and other assertion methods to verify that mocks were called as expected.
Interview Tip
When discussing patching in interviews, emphasize your understanding of its purpose: isolating code for more reliable and controlled testing. Explain how patching allows you to simulate different scenarios, including error conditions, without relying on external systems. Be prepared to discuss the challenges of patching, such as identifying the correct patch target, and the importance of reverting patches to avoid side effects.
When to Use Patching
Use patching when you need to:
Memory Footprint
Patching itself doesn't have a significant memory footprint. The mock objects created by unittest.mock
are relatively lightweight. However, if you create a large number of mocks or store a lot of data within the mock objects (e.g., using side_effect
to return large datasets), the memory usage can increase.
Alternatives
While patching is a powerful technique, there are alternatives to consider:
Pros
Cons
FAQ
-
Why is my patch not working?
The most common reason is an incorrect patch target. Double-check that you are patching the object where it is looked up, not where it is defined. Also, make sure the module path is correct.
-
How can I verify that my mock was called with specific arguments?
Use the
mock.assert_called_with(*args, **kwargs)
method to verify that the mock was called with the expected arguments. -
How do I mock a method that is called multiple times with different arguments?
You can use the
side_effect
attribute of the mock object to specify a list of return values or a function that will be called each time the mock is invoked. The function can then return different values based on the arguments passed to it. -
Can I mock built-in functions like
open()
ortime.sleep()
?
Yes, you can mock built-in functions using
unittest.mock.patch
. Just specify the correct fully qualified name of the built-in function (e.g.,'__builtin__.open'
for Python 2 or'builtins.open'
for Python 3).