Mocking and Patching
Mocking replaces parts of your system with fake objects during testing. This isolates your tests from external dependencies like databases, APIs, or file systems.
monkeypatch Fixture
pytest's built-in monkeypatch fixture modifies objects, attributes, and environment variables safely:
import os
def get_api_url():
return os.environ.get("API_URL", "http://localhost:8000")
def test_custom_api_url(monkeypatch):
monkeypatch.setenv("API_URL", "http://staging.example.com")
assert get_api_url() == "http://staging.example.com"
def test_default_api_url(monkeypatch):
monkeypatch.delenv("API_URL", raising=False)
assert get_api_url() == "http://localhost:8000"
Patching Functions
Use monkeypatch.setattr to replace function implementations:
import datetime
def get_today():
return datetime.date.today()
def test_specific_date(monkeypatch):
class FakeDate:
@staticmethod
def today():
return datetime.date(2025, 1, 1)
monkeypatch.setattr(datetime, "date", FakeDate)
assert get_today() == datetime.date(2025, 1, 1)
unittest.mock
For more complex scenarios, use Python's unittest.mock module alongside pytest:
from unittest.mock import Mock, patch
def test_api_call():
with patch("my_module.requests.get") as mock_get:
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {"result": "ok"}
response = fetch_data()
assert response == {"result": "ok"}
mock_get.assert_called_once_with("https://api.example.com")
Mock Best Practices
- Mock at the boundary - mock external services, not internal logic.
- Keep mocks simple - verify behavior, not implementation details.
- Use
monkeypatchfor simple replacements,unittest.mockfor complex ones. - Always prefer real objects when they are fast and deterministic.