Feature
Mocking
Patching
Understanding Patching: How It Works
Patching temporarily replaces the actual implementation of a method, function, or object with a mock during testing.
Base:
# function_to_test.py
def send_email(email_address, message):
email_service.send(email=email_address, message=message)
from unittest.mock import Mock
import function_to_test
def test_send_email():
mock_email_service = Mock()
function_to_test.email_service = mock_email_service
function_to_test.send_email("test@example.com", "Hello")
mock_email_service.send.assert_called_with(email="test@example.com", message="Hello")
In the above code, we use the mocker
fixture provided by pytest-mock
to patch the email_service.send
method in the function_to_test
module. It automatically handles the start and end of the patching, meaning the original method is restored after the test, which helps prevent side effects on other tests.
TLDR
Feature | Mocking | Patching |
---|---|---|
Purpose | Simulate behavior or state of objects/functions | Temporarily replace objects/functions in their specific use context |
Focus | Emphasizes behavior and interactions | Focuses on substituting specific components during test execution |
Implementation | Typically implemented with mock libraries (e.g., unittest.mock ) | Also implemented using the same mock libraries with methods like patch() |
Isolation | Isolates a unit from external dependencies for testing | Isolates and replaces specific components temporarily |
Flexibility | Offers flexibility in simulating various behaviors | Flexible in substituting and controlling components during test runtime |
Replaces code | Replaces the behavior of the target object | Temporarily overrides the target object in its specific use context |
Behavior control | High degree of control over the mocked object’s behavior | Allows control over the replaced object’s return value and side effects |
Common Use Cases | Useful for testing complex interactions and dependencies | Ideal for controlling external dependencies like APIs during testing |
Best Practices
Let’s quickly go through some essential best practices when using mocking or patching in your unit tests.
-
Use mocks and patches for external dependencies - Focus on isolating parts of the system that interact with external services like databases, web APIs, or third-party libraries.
-
Ensure that mocks mimic the behavior of the actual dependencies they replace as closely as possible.
-
Review API and other documentation regularly to ensure that mocks remain accurate and honour the contract.
-
Use tools that automatically unpatch or restore the original state of the object after each test to prevent side effects - Pytest fixtures with setup teardown,
monkeypatch
fixture orunittest.mock.patch
fixtures. -
Document why a mock or patch is necessary for understanding the context and purpose of the test with descriptive names.
-
When using mocking, verify the interaction between functions or objects, ensuring that the mock object was called with expected arguments. You can use methods like
assert_called_once_with()
orassert_called_with()
as detailed in this guide. -
Overusing mocking or patching may lead to overly complex tests and decrease the readability and maintainability of your codebase.