Python's unittest.mock module lets you replace parts of your system during tests. Essential for isolating code and testing without real dependencies.

Basic Mock

from unittest.mock import Mock
 
# Create a mock
mock_db = Mock()
 
# Configure return values
mock_db.get_user.return_value = {'id': 1, 'name': 'Alice'}
 
# Use it
result = mock_db.get_user(1)
print(result)  # {'id': 1, 'name': 'Alice'}
 
# Verify calls
mock_db.get_user.assert_called_once_with(1)

MagicMock

Includes default implementations of magic methods:

from unittest.mock import MagicMock
 
mock = MagicMock()
 
# Magic methods work automatically
len(mock)      # Returns MagicMock
iter(mock)     # Returns iterator
mock[0]        # Returns MagicMock
mock['key']    # Returns MagicMock
 
# Configure magic methods
mock.__len__.return_value = 5
print(len(mock))  # 5

patch Decorator

Replace objects during tests:

from unittest.mock import patch
 
# In myapp.py
import requests
def fetch_data(url):
    return requests.get(url).json()
 
# In test_myapp.py
from unittest.mock import patch
 
@patch('myapp.requests.get')
def test_fetch_data(mock_get):
    mock_get.return_value.json.return_value = {'data': 'test'}
    
    result = fetch_data('http://api.example.com')
    
    assert result == {'data': 'test'}
    mock_get.assert_called_once_with('http://api.example.com')

patch Context Manager

from unittest.mock import patch
 
def test_something():
    with patch('myapp.external_service') as mock_service:
        mock_service.call.return_value = 'mocked'
        
        result = my_function()
        
        assert result == 'mocked'

patch.object

Patch an attribute on an object:

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

Patching Multiple Objects

from unittest.mock import patch
 
@patch('myapp.service_a')
@patch('myapp.service_b')
def test_function(mock_b, mock_a):  # Note: reverse order!
    mock_a.call.return_value = 'a'
    mock_b.call.return_value = 'b'
    
    result = my_function()
 
# Or with context managers
def test_function():
    with patch('myapp.service_a') as mock_a, \
         patch('myapp.service_b') as mock_b:
        # test code
        pass

Configuring Return Values

from unittest.mock import Mock
 
mock = Mock()
 
# Simple return
mock.method.return_value = 42
 
# Different returns each call
mock.method.side_effect = [1, 2, 3]
print(mock.method())  # 1
print(mock.method())  # 2
print(mock.method())  # 3
 
# Raise exception
mock.method.side_effect = ValueError("error")
 
# Callable side effect
def custom_logic(x):
    return x * 2
mock.method.side_effect = custom_logic
print(mock.method(5))  # 10

Assertions

from unittest.mock import Mock, call
 
mock = Mock()
 
# Call verification
mock.method(1, 2, key='value')
 
mock.method.assert_called()
mock.method.assert_called_once()
mock.method.assert_called_with(1, 2, key='value')
mock.method.assert_called_once_with(1, 2, key='value')
 
# Multiple calls
mock.method(1)
mock.method(2)
mock.method(3)
 
mock.method.assert_any_call(2)  # Was it ever called with 2?
assert mock.method.call_count == 3
 
# Verify call order
mock.method.assert_has_calls([
    call(1),
    call(2),
    call(3)
])

spec and autospec

Prevent typos and ensure mock matches real API:

from unittest.mock import Mock, create_autospec
 
class RealClass:
    def method(self, x, y):
        return x + y
 
# Basic spec
mock = Mock(spec=RealClass)
mock.method(1, 2)        # OK
mock.nonexistent()       # AttributeError!
 
# Autospec - also checks signatures
mock = create_autospec(RealClass)
mock.method(1, 2)        # OK
mock.method(1)           # TypeError - missing argument!

Patching Properties

from unittest.mock import patch, PropertyMock
 
class MyClass:
    @property
    def value(self):
        return expensive_computation()
 
with patch.object(MyClass, 'value', new_callable=PropertyMock) as mock_value:
    mock_value.return_value = 42
    obj = MyClass()
    print(obj.value)  # 42

Async Mocks

from unittest.mock import AsyncMock, patch
import asyncio
 
async def fetch_data():
    return await external_api()
 
@patch('myapp.external_api', new_callable=AsyncMock)
async def test_fetch(mock_api):
    mock_api.return_value = {'data': 'test'}
    
    result = await fetch_data()
    
    assert result == {'data': 'test'}
 
# Run with
asyncio.run(test_fetch())

Mocking Datetime

from unittest.mock import patch
from datetime import datetime
 
def get_greeting():
    hour = datetime.now().hour
    if hour < 12:
        return "Good morning"
    return "Good afternoon"
 
@patch('myapp.datetime')
def test_morning(mock_datetime):
    mock_datetime.now.return_value.hour = 9
    assert get_greeting() == "Good morning"
 
@patch('myapp.datetime')
def test_afternoon(mock_datetime):
    mock_datetime.now.return_value.hour = 15
    assert get_greeting() == "Good afternoon"

Mocking File Operations

from unittest.mock import patch, mock_open
 
def read_config(path):
    with open(path) as f:
        return f.read()
 
def test_read_config():
    mock_data = "key=value"
    
    with patch('builtins.open', mock_open(read_data=mock_data)):
        result = read_config('config.txt')
    
    assert result == "key=value"

Mocking Environment Variables

from unittest.mock import patch
import os
 
@patch.dict(os.environ, {'API_KEY': 'test-key'})
def test_with_env():
    assert os.environ['API_KEY'] == 'test-key'

Reset Mocks

from unittest.mock import Mock
 
mock = Mock()
mock.method(1)
mock.method(2)
 
# Reset all call info
mock.reset_mock()
 
assert mock.method.call_count == 0
mock.method.assert_not_called()

Common Patterns

Database Mock

from unittest.mock import Mock
 
def get_user_orders(db, user_id):
    user = db.get_user(user_id)
    return db.get_orders(user['id'])
 
def test_get_user_orders():
    mock_db = Mock()
    mock_db.get_user.return_value = {'id': 1, 'name': 'Alice'}
    mock_db.get_orders.return_value = [{'id': 100}, {'id': 101}]
    
    orders = get_user_orders(mock_db, 1)
    
    assert len(orders) == 2
    mock_db.get_user.assert_called_once_with(1)

HTTP Client Mock

from unittest.mock import patch, Mock
 
@patch('requests.get')
def test_api_call(mock_get):
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {'success': True}
    mock_get.return_value = mock_response
    
    result = call_api('/endpoint')
    
    assert result['success'] is True

Summary

Key mocking patterns:

  • Mock/MagicMock: Create mock objects
  • patch: Replace during tests (decorator or context manager)
  • return_value: Configure what mock returns
  • side_effect: Multiple returns, exceptions, or custom logic
  • spec/autospec: Ensure mock matches real API
  • assert_called_*: Verify interactions

Mocking isolates your code from dependencies, making tests fast and reliable.

React to this post: