Python tutorials > Testing > Mocking > How to use `unittest.mock`?
How to use `unittest.mock`?
The unittest.mock
library in Python is a powerful tool for isolating units of code during testing. It allows you to replace parts of your system under test with mock objects, which you can then use to control and inspect how those parts are being used. This tutorial provides a comprehensive guide to using unittest.mock
, covering its core concepts, practical examples, and best practices.
Introduction to Mocking
Mocking is a testing technique where real dependencies are replaced with controlled substitutes (mocks). This is useful when:
unittest.mock
provides classes like Mock
and MagicMock
that allow you to create mock objects and configure their behavior.
Basic Mocking with `Mock`
This example demonstrates how to use the Mock
class to create a mock object, configure its get_data
method to return a specific value, and then use the mock object in a function. The assert_called_once
method is used to verify that the get_data
method was called exactly once.
from unittest.mock import Mock
def my_function(dependency):
return dependency.get_data()
# Create a mock object
mock_dependency = Mock()
# Configure the mock object's return value
mock_dependency.get_data.return_value = "Mocked data"
# Call the function with the mock dependency
result = my_function(mock_dependency)
# Assert that the function returns the expected value
print(result) # Output: Mocked data
# Assert that the method was called
mock_dependency.get_data.assert_called_once()
Using `MagicMock`
MagicMock
is a subclass of Mock
that provides magic methods (e.g., __str__
, __len__
) that are useful for mocking objects that are expected to behave like real Python objects. It provides sane defaults for magic methods so you do not have to configure them manually. It is designed to make mocking easier and more intuitive.
from unittest.mock import MagicMock
def add(x, y):
return x + y
mock_add = MagicMock(return_value=5)
result = mock_add(2, 3)
print(result)
print(mock_add.call_args)
# MagicMock will work even if the method does not exist
mock_non_existent = MagicMock()
mock_non_existent.my_method(1, 2, key='value')
print(mock_non_existent.my_method.call_args)
# Output
# 5
# call(2, 3)
# call(1, 2, key='value')
Patching Objects with `patch`
The patch
decorator (or context manager) allows you to temporarily replace an object with a mock object during a test. This is useful for mocking objects that are used in multiple functions or modules. In this example, the DatabaseConnection
class in the my_module
module is replaced with a mock object during the test. The decorator is called with the fully qualified name of the object to be replaced.
from unittest.mock import patch
import my_module # Assume my_module contains a function that uses a database connection
@patch('my_module.DatabaseConnection')
def test_my_function(mock_database_connection):
# Configure the mock database connection
mock_database_connection.return_value.query.return_value = [('data',)]
# Call the function that uses the database connection
result = my_module.my_function()
# Assert that the function returns the expected value
assert result == [('data',)]
# Assert that the query method was called
mock_database_connection.return_value.query.assert_called_once()
Concepts Behind the Snippet
Dependency Injection: Mocking works best when your code is designed with dependency injection in mind. This means that dependencies are passed into functions or classes rather than being hardcoded. This makes it easier to replace dependencies with mock objects during testing. Test Isolation: The goal of mocking is to isolate the unit of code you're testing from its dependencies. This ensures that failures are due to the unit under test and not its dependencies. Behavior Verification: Mocking allows you to verify that the unit under test interacts with its dependencies in the expected way. This includes verifying that methods are called with the correct arguments and that they are called the correct number of times.
Real-Life Use Case
Consider a function that sends an email: During testing, you don't want to actually send emails. You can use mocking to replace the def send_notification_email(user, message):
email_service = EmailService()
email_service.send_email(user.email, message)
EmailService
with a mock object and verify that the send_email
method is called with the correct arguments.@patch('your_module.EmailService')
def test_send_notification_email(mock_email_service):
user = User(email='test@example.com')
send_notification_email(user, 'Test message')
mock_email_service.return_value.send_email.assert_called_with('test@example.com', 'Test message')
Best Practices
Mock only what you need to: Avoid mocking objects that are part of the standard library or that are simple value objects. Only mock dependencies that are complex or have side effects. Use descriptive names for mock objects: This makes it easier to understand what the mock object is representing. Verify interactions with mock objects: Use the Keep tests small and focused: Each test should focus on testing a specific aspect of the unit under test. Avoid over-mocking: Mocking too much can make your tests brittle and difficult to maintain. Try to strike a balance between isolating the unit under test and testing its integration with its dependencies.assert_called
methods to verify that the unit under test interacts with its dependencies in the expected way.
Interview Tip
When discussing mocking in an interview, be sure to explain the purpose of mocking (isolating units of code during testing), the different types of mock objects (e.g., Mock
, MagicMock
), and the different ways to use mocking (e.g., patch
decorator, context manager). Be prepared to provide examples of how you have used mocking in your own projects.
When to use them
Use unittest.mock
when:
Memory footprint
Mock objects generally have a small memory footprint. However, creating a large number of mock objects or mocking objects with complex structures can increase memory usage. It's generally not a concern, but it's something to be aware of if you're working with very large codebases.
Alternatives
Other mocking libraries in Python include:
unittest.mock
in pytest tests.
Pros
Cons
FAQ
-
What is the difference between `Mock` and `MagicMock`?
MagicMock
is a subclass ofMock
that provides magic methods (e.g.,__str__
,__len__
) that are useful for mocking objects that are expected to behave like real Python objects. -
How do I verify that a method was called with specific arguments?
Use the
assert_called_with
method of the mock object. For example:mock_object.method.assert_called_with(arg1, arg2)
. -
How do I mock a property?
You can use the
PropertyMock
class to mock a property. For example:from unittest.mock import PropertyMock @patch('your_module.MyClass.my_property', new_callable=PropertyMock) def test_my_function(mock_my_property): mock_my_property.return_value = 'mocked_value' # ...