The unittest.mock module lets you replace parts of your system during tests. Essential for isolating units and testing code that depends on external services.

Basic Mock

from unittest.mock import Mock
 
# Create a mock object
mock = Mock()
 
# Call it with any arguments
result = mock(1, 2, key='value')
 
# Mock returns another Mock by default
print(result)  # <Mock ...>
 
# Access attributes (also Mocks)
print(mock.some_attribute)  # <Mock ...>
 
# Check how it was called
mock.assert_called_once_with(1, 2, key='value')
print(mock.call_count)  # 1
print(mock.call_args)   # call(1, 2, key='value')

Configuring Return Values

from unittest.mock import Mock
 
# Set return value
mock = Mock(return_value=42)
print(mock())  # 42
 
# Different return values for consecutive calls
mock = Mock(side_effect=[1, 2, 3])
print(mock())  # 1
print(mock())  # 2
print(mock())  # 3
 
# Raise exception
mock = Mock(side_effect=ValueError("error"))
# mock()  # Raises ValueError
 
# Custom function
mock = Mock(side_effect=lambda x: x * 2)
print(mock(5))  # 10

MagicMock

Mock with magic methods pre-configured:

from unittest.mock import MagicMock
 
mock = MagicMock()
 
# Magic methods work
len(mock)           # Returns 0 (default)
mock[0]             # Returns MagicMock
mock.__str__()      # '<MagicMock ...>'
 
# Configure magic methods
mock.__len__.return_value = 5
print(len(mock))    # 5
 
mock.__getitem__.return_value = 'value'
print(mock['key'])  # 'value'

patch Decorator

Replace objects during test:

from unittest.mock import patch
 
# Module: myapp.py
# import requests
# def fetch_data(url):
#     response = requests.get(url)
#     return response.json()
 
# Test file
@patch('myapp.requests.get')
def test_fetch_data(mock_get):
    # Configure mock response
    mock_response = Mock()
    mock_response.json.return_value = {'key': 'value'}
    mock_get.return_value = mock_response
    
    # Call function
    from myapp import fetch_data
    result = fetch_data('http://api.example.com')
    
    # Assert
    mock_get.assert_called_once_with('http://api.example.com')
    assert result == {'key': 'value'}

patch as 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 = function_under_test()
        
        assert result == 'expected'
        mock_service.call.assert_called()

patch.object

Patch attribute of 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'

patch.dict

Patch dictionary values:

from unittest.mock import patch
import os
 
with patch.dict(os.environ, {'API_KEY': 'test_key'}):
    print(os.environ['API_KEY'])  # 'test_key'
 
# Clear values
with patch.dict(os.environ, {'DEBUG': '1'}, clear=True):
    print(os.environ)  # Only contains DEBUG

Assertions

from unittest.mock import Mock, call
 
mock = Mock()
 
# Call the mock
mock(1, 2)
mock(3, 4)
mock('a', b='c')
 
# Assert calls
mock.assert_called()              # Called at least once
mock.assert_called_once()         # Fails (called 3 times)
mock.assert_called_with('a', b='c')  # Last call matches
 
# Check all calls
assert mock.call_args_list == [
    call(1, 2),
    call(3, 4),
    call('a', b='c'),
]
 
# Assert any call
mock.assert_any_call(3, 4)
 
# Assert not called
fresh_mock = Mock()
fresh_mock.assert_not_called()

spec for Type Safety

from unittest.mock import Mock, create_autospec
 
class Database:
    def connect(self): pass
    def query(self, sql: str) -> list: pass
    def close(self): pass
 
# Without spec: allows any attribute
mock = Mock()
mock.nonexistent()  # Works (bad!)
 
# With spec: only allows existing attributes
mock = Mock(spec=Database)
mock.connect()      # Works
# mock.nonexistent()  # AttributeError!
 
# autospec also checks signatures
mock = create_autospec(Database)
mock.query("SELECT *")    # Works
# mock.query()              # TypeError: missing argument

Patching Where Imported

# myapp.py
from requests import get
 
def fetch():
    return get('http://example.com')
 
# test_myapp.py
# WRONG: patches requests module, but myapp imported 'get' directly
@patch('requests.get')
def test_wrong(mock_get): pass
 
# RIGHT: patch where it's used
@patch('myapp.get')
def test_right(mock_get): pass

PropertyMock

Mock properties:

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

AsyncMock (Python 3.8+)

from unittest.mock import AsyncMock, patch
import asyncio
 
async def async_function():
    return 'real'
 
async def test_async():
    mock = AsyncMock(return_value='mocked')
    result = await mock()
    assert result == 'mocked'
    
    # With patch
    with patch('myapp.async_function', new_callable=AsyncMock) as mock_func:
        mock_func.return_value = 'mocked'
        result = await async_function()

Mock Methods Chaining

from unittest.mock import Mock
 
# Mock chained calls
mock = Mock()
mock.method1().method2().method3.return_value = 'result'
 
result = mock.method1().method2().method3
print(result)  # 'result'
 
# Using configure_mock
mock = Mock()
mock.configure_mock(**{
    'method.return_value.data': [1, 2, 3]
})
print(mock.method().data)  # [1, 2, 3]

reset_mock

from unittest.mock import Mock
 
mock = Mock(return_value=10)
mock(1, 2)
mock(3, 4)
 
# Reset call history
mock.reset_mock()
print(mock.call_count)  # 0
print(mock())           # 10 (return_value preserved)
 
# Reset everything
mock.reset_mock(return_value=True)
print(mock())           # <Mock ...>

Practical: Testing API Client

from unittest.mock import Mock, patch
import pytest
 
class APIClient:
    def __init__(self, base_url: str):
        self.base_url = base_url
    
    def get_user(self, user_id: int) -> dict:
        import requests
        response = requests.get(f"{self.base_url}/users/{user_id}")
        response.raise_for_status()
        return response.json()
 
@patch('requests.get')
def test_get_user_success(mock_get):
    # Arrange
    mock_response = Mock()
    mock_response.json.return_value = {'id': 1, 'name': 'Alice'}
    mock_response.raise_for_status = Mock()
    mock_get.return_value = mock_response
    
    # Act
    client = APIClient('http://api.example.com')
    user = client.get_user(1)
    
    # Assert
    assert user == {'id': 1, 'name': 'Alice'}
    mock_get.assert_called_once_with('http://api.example.com/users/1')
 
@patch('requests.get')
def test_get_user_error(mock_get):
    mock_get.side_effect = requests.HTTPError("404")
    
    client = APIClient('http://api.example.com')
    
    with pytest.raises(requests.HTTPError):
        client.get_user(999)

Common Patterns

PatternUse
Mock(return_value=x)Fixed return
Mock(side_effect=[...])Sequential returns
Mock(side_effect=func)Dynamic return
Mock(spec=Class)Type-safe mock
@patch('module.name')Replace during test
patch.object(obj, 'attr')Patch instance
patch.dict(d, values)Patch dict
create_autospec(Class)Mock with signatures

Mocking isolates your tests from external dependencies. Use it to make tests fast, deterministic, and focused.

React to this post: