The unittest.mock module lets you replace parts of your system during tests. Here's how to use it effectively.

Basic Mocking

from unittest.mock import Mock, MagicMock
 
# Create a mock object
mock = Mock()
 
# Call it like any object
mock.some_method(1, 2, 3)
mock.attribute = "value"
 
# Check what happened
mock.some_method.assert_called_once_with(1, 2, 3)
print(mock.attribute)  # "value"
 
# MagicMock supports magic methods
magic = MagicMock()
print(len(magic))  # 0 (default)
magic.__len__.return_value = 5
print(len(magic))  # 5

Return Values

from unittest.mock import Mock
 
mock = Mock()
 
# Simple return value
mock.method.return_value = 42
assert mock.method() == 42
 
# Different returns for multiple calls
mock.method.side_effect = [1, 2, 3]
assert mock.method() == 1
assert mock.method() == 2
assert mock.method() == 3
 
# Raise an exception
mock.method.side_effect = ValueError("error")
# mock.method()  # Raises ValueError
 
# Dynamic return based on input
def dynamic_return(x):
    return x * 2
 
mock.method.side_effect = dynamic_return
assert mock.method(5) == 10

Patching

from unittest.mock import patch
 
# Patch a module attribute
@patch("mymodule.requests.get")
def test_api_call(mock_get):
    mock_get.return_value.json.return_value = {"status": "ok"}
    
    result = mymodule.fetch_data()
    
    mock_get.assert_called_once()
    assert result["status"] == "ok"
 
# Context manager form
def test_with_context():
    with patch("mymodule.requests.get") as mock_get:
        mock_get.return_value.status_code = 200
        # Test code here
 
# Decorator stacking (applied bottom-up)
@patch("mymodule.func_c")
@patch("mymodule.func_b")
@patch("mymodule.func_a")
def test_multiple(mock_a, mock_b, mock_c):
    # mock_a patches func_a, etc.
    pass

Where to Patch

# mymodule.py
from os.path import exists
 
def check_file(path):
    return exists(path)
 
# test_mymodule.py
from unittest.mock import patch
 
# WRONG: Patches os.path.exists globally
@patch("os.path.exists")
def test_wrong(mock_exists):
    pass
 
# RIGHT: Patch where it's used (imported into mymodule)
@patch("mymodule.exists")
def test_right(mock_exists):
    mock_exists.return_value = True
    assert check_file("/some/path") == True

Spec and Autospec

from unittest.mock import Mock, create_autospec
 
class RealClass:
    def method(self, x: int) -> str:
        return str(x)
 
# Without spec: accepts any attribute/method
mock = Mock()
mock.nonexistent_method()  # Works (wrong!)
 
# With spec: only allows real attributes
mock = Mock(spec=RealClass)
# mock.nonexistent_method()  # AttributeError
 
# Autospec: also checks signatures
mock = create_autospec(RealClass)
# mock.method()  # TypeError: missing argument 'x'
mock.method(42)  # Works

patch.object

from unittest.mock import patch
 
class MyClass:
    def method(self):
        return "real"
 
obj = MyClass()
 
# Patch instance method
with patch.object(obj, "method", return_value="mocked"):
    assert obj.method() == "mocked"
 
# Patch class method
with patch.object(MyClass, "method", return_value="mocked"):
    assert MyClass().method() == "mocked"

patch.dict

from unittest.mock import patch
import os
 
# Patch environment variables
with patch.dict(os.environ, {"API_KEY": "test-key"}):
    assert os.environ["API_KEY"] == "test-key"
 
# Clear and set
with patch.dict(os.environ, {"NEW_VAR": "value"}, clear=True):
    assert "PATH" not in os.environ
    assert os.environ["NEW_VAR"] == "value"

Assertions

from unittest.mock import Mock, call
 
mock = Mock()
mock(1, 2, key="value")
mock(3, 4)
 
# Was called at all
mock.assert_called()
 
# Called at least once with specific args
mock.assert_any_call(1, 2, key="value")
 
# Most recent call
mock.assert_called_with(3, 4)
 
# Called exactly once
# mock.assert_called_once()  # Fails: called twice
 
# Specific call count
assert mock.call_count == 2
 
# Check all calls
assert mock.call_args_list == [
    call(1, 2, key="value"),
    call(3, 4),
]
 
# Reset mock
mock.reset_mock()
assert mock.call_count == 0

PropertyMock

from unittest.mock import patch, PropertyMock
 
class Config:
    @property
    def api_url(self):
        return "https://api.example.com"
 
# Patch a property
with patch.object(
    Config, "api_url", new_callable=PropertyMock
) as mock_url:
    mock_url.return_value = "https://test.example.com"
    
    config = Config()
    assert config.api_url == "https://test.example.com"

AsyncMock

from unittest.mock import AsyncMock, patch
import asyncio
 
async def fetch_data(client):
    return await client.get("/data")
 
# Mock async functions
mock_client = AsyncMock()
mock_client.get.return_value = {"result": "ok"}
 
# Test async code
async def test_fetch():
    result = await fetch_data(mock_client)
    assert result == {"result": "ok"}
    mock_client.get.assert_awaited_once_with("/data")
 
asyncio.run(test_fetch())
 
# Patch async function
@patch("mymodule.async_func", new_callable=AsyncMock)
async def test_with_patch(mock_func):
    mock_func.return_value = "mocked"
    result = await mymodule.async_func()
    assert result == "mocked"

Common Patterns

from unittest.mock import Mock, patch, ANY
 
# Accept any argument
mock = Mock()
mock("specific", ANY, key=ANY)
mock.assert_called_with("specific", ANY, key=ANY)
 
# Sentinel objects (unique placeholders)
from unittest.mock import sentinel
mock = Mock(return_value=sentinel.RESULT)
assert mock() is sentinel.RESULT
 
# Wrap real implementation
real_func = lambda x: x * 2
 
mock = Mock(wraps=real_func)
assert mock(5) == 10  # Calls real function
mock.assert_called_with(5)  # But also tracks calls

Testing External APIs

from unittest.mock import patch, Mock
import requests
 
def get_user(user_id):
    response = requests.get(f"https://api.example.com/users/{user_id}")
    response.raise_for_status()
    return response.json()
 
@patch("requests.get")
def test_get_user(mock_get):
    # Setup mock response
    mock_response = Mock()
    mock_response.json.return_value = {"id": 1, "name": "Test User"}
    mock_response.raise_for_status = Mock()
    mock_get.return_value = mock_response
    
    # Test
    user = get_user(1)
    
    # Verify
    mock_get.assert_called_once_with("https://api.example.com/users/1")
    assert user["name"] == "Test User"
 
@patch("requests.get")
def test_get_user_error(mock_get):
    mock_get.return_value.raise_for_status.side_effect = (
        requests.HTTPError("404")
    )
    
    with pytest.raises(requests.HTTPError):
        get_user(999)

Testing with Databases

from unittest.mock import patch, MagicMock
 
class UserRepository:
    def __init__(self, db):
        self.db = db
    
    def get_user(self, user_id):
        return self.db.query(User).filter_by(id=user_id).first()
 
def test_get_user():
    # Mock the database session
    mock_db = MagicMock()
    mock_user = User(id=1, name="Test")
    mock_db.query.return_value.filter_by.return_value.first.return_value = mock_user
    
    repo = UserRepository(mock_db)
    user = repo.get_user(1)
    
    assert user.name == "Test"
    mock_db.query.assert_called_with(User)

Best Practices

# 1. Use spec/autospec to catch API changes
mock = Mock(spec=RealClass)
 
# 2. Patch at the point of use, not definition
@patch("mymodule.dependency")  # Not "dependency_module.func"
 
# 3. Keep patches minimal
# Bad: Mock entire class when you only need one method
# Good: Mock just the method you need
 
# 4. Verify interactions, not implementation
mock.assert_called()  # Good: verifies behavior
# Checking internal state of mock: often too coupled
 
# 5. Use fixtures for common mocks (pytest)
@pytest.fixture
def mock_api():
    with patch("mymodule.api_client") as mock:
        mock.get.return_value = {"status": "ok"}
        yield mock

Mocking isolates your tests from external dependencies. Use it to test your code's behavior, not the behavior of the things it calls.

React to this post: