unittest.mock.patch
unittest.mock.patch
Section titled βunittest.mock.patchβWhat it is
Section titled βWhat it isβunittest.mock.patch temporarily replaces an attribute, function, or object β for the duration of a test β with a Mock (or a value you specify), then automatically restores the original afterward. Itβs how you test code that calls something you donβt want to actually run in a test: an API request, a database write, datetime.now(), os.remove.
Before this feature
Section titled βBefore this featureβWithout patching, testing code that calls an external service means either actually calling it (slow, flaky, needs network/credentials) or manually saving and restoring the original function yourself:
import requests
def get_username(user_id): response = requests.get(f"https://api.example.com/users/{user_id}") return response.json()["name"]
# Testing this directly makes a real HTTP call every time you run the testAfter this feature
Section titled βAfter this featureβfrom unittest.mock import patch
def get_username(user_id): response = requests.get(f"https://api.example.com/users/{user_id}") return response.json()["name"]
@patch("requests.get")def test_get_username(mock_get): mock_get.return_value.json.return_value = {"name": "Alice"} result = get_username(42) assert result == "Alice" mock_get.assert_called_once_with("https://api.example.com/users/42")# after the test, requests.get is automatically restored to the real function# As a context manager, for patching only part of a test functiondef test_partial_patch(): with patch("requests.get") as mock_get: mock_get.return_value.json.return_value = {"name": "Bob"} assert get_username(1) == "Bob" # requests.get is back to normal here, outside the `with` blockWhy this is better
Section titled βWhy this is betterβ- Isolates the unit under test:
get_usernameβs logic is tested without a real network call β fast, deterministic, no external dependency needed to run the test suite. - Automatic cleanup: whether used as a decorator or context manager, the original is guaranteed restored even if the test raises an exception.
- Built-in call assertions:
mock_get.assert_called_once_with(...)verifies not just the return value, but that the mocked function was called correctly.
Key notes / edge cases
Section titled βKey notes / edge casesβ- Patch where itβs looked up, not where itβs defined:
@patch("requests.get")patches the name inside therequestsmodule. Ifget_usernamedidfrom requests import getinstead, youβd need@patch("mymodule.get")β patching the name in the module that imported it, since thatβs the reference actually being called. - The patched object is replaced with a
MagicMockby default, which auto-creates attributes/methods on access β convenient, but it also means a typo likemock_get.return_vlauesilently creates a new mock attribute instead of raising an error. patch.object(SomeClass, "method_name")patches a method on a specific class instead of a dotted string path β useful when the string-path form is awkward.- Multiple
@patchdecorators apply bottom-up, and their corresponding mock arguments are injected in the same bottom-up order β a common source of confusion when stacking several patches.
Quick practice
Section titled βQuick practiceβ-
What does
unittest.mock.patchguarantee happens after the test, even if it fails?Answer
The original object/function is automatically restored β patching is always undone, decorator or context-manager form. -
If
mymodule.pydoesfrom requests import getand callsget(...), what do you patch β"requests.get"or"mymodule.get"?Answer
`"mymodule.get"` β you patch the name where it's *looked up* (the importing module's namespace), not where it was originally defined. -
How do you assert that a mocked function was called with specific arguments?
Answer
`mock_obj.assert_called_once_with(expected_args)` (or `assert_called_with` if it may be called more than once).