patch & patch.object Decorators
patch() temporarily replaces objects during tests, restoring them when the test
finishes. It can be used as a decorator, context manager, or direct call.
patch as a Decorator
import unittest
from unittest.mock import patch
class TestUserService(unittest.TestCase):
@patch('myapp.services.email.send_email')
def test_welcome_email(self, mock_send_email):
mock_send_email.return_value = True
user = create_user("alice@example.com")
mock_send_email.assert_called_once_with(
to="alice@example.com",
subject="Welcome!",
body="Hello Alice"
)
patch as a Context Manager
class TestConfig(unittest.TestCase):
def test_production_mode(self):
with patch('myapp.config.DEBUG', False):
config = get_config()
self.assertFalse(config.debug)
def test_api_endpoint(self):
with patch('myapp.services.api.BASE_URL', 'http://test-server'):
response = call_api('/users')
self.assertIsNotNone(response)
patch.object
class TestPayment(unittest.TestCase):
def test_charge_card(self):
gateway = PaymentGateway()
with patch.object(gateway, 'charge', return_value=True):
processor = PaymentProcessor(gateway)
result = processor.charge_card(amount=100)
self.assertTrue(result)
gateway.charge.assert_called_once_with(amount=100)
Patch Target Paths
# patch the object WHERE IT IS USED, not where it's defined
# Correct: patches the name in myapp.services module
@patch('myapp.services.email.send_email')
def test_send(mock_send):
pass
# Common pattern: mock at the import location
# If myapp.views imports email:
# from myapp.services.email import send_email
# Then patch 'myapp.views.send_email'
Key Takeaway
patch() and patch.object() replace objects during tests.
Always patch where the object is used, not where it's defined. Use decorators
for simple cases, context managers for multiple patches.