Unittest.mock.Mock: примеры (PYTHON)
unittest.mock.Mock(spec, spec_set, side_effect, return_value, wraps, name, unsafe, kwargs): unittest.mock.MockБазовое описание unittest.mock.Mock
Класс unittest.mock.Mock представляет собой гибкий объект для создания заглушек в тестировании Python. Он применяется для имитации поведения реальных объектов, заменяя их во время тестов.
Основные сценарии использования включают замену внешних зависимостей, симуляцию исключительных ситуаций, проверку взаимодействий между компонентами и изоляцию тестируемого кода.
Аргументы конструктора
- spec - задает спецификацию объекта для ограничения доступных атрибутов
- return_value - значение, возвращаемое при вызове объекта
- side_effect - функция, исключение или итерируемый объект для определения поведения при вызовах
- wraps - объект, который оборачивается Mock'ом
- name - строковое имя объекта для отладки
- unsafe - разрешает доступ к атрибутам, начинающимся с 'assert' или 'assret'
Экземпляры Mock отслеживают все обращения к атрибутам и вызовы методов. Они автоматически создают новые Mock-объекты для запрашиваемых атрибутов.
Основные примеры использования
Простой Mock с возвращаемым значением:
from unittest.mock import Mock
mock_obj = Mock(return_value=42)
result = mock_obj()
print(result)42
Mock с побочным эффектом:
mock_iter = Mock(side_effect=[1, 2, 3])
print(mock_iter())
print(mock_iter())
print(mock_iter())1 2 3
Mock с имитацией исключения:
mock_error = Mock(side_effect=ValueError('Ошибка'))
try:
mock_error()
except ValueError as e:
print(e)Ошибка
Mock со спецификацией:
class RealClass:
def method(self):
pass
mock_spec = Mock(spec=RealClass)
print(mock_spec.method)
print(mock_spec.undefined_attr)<Mock name='mock.method' id='...'> AttributeError: Mock object has no attribute 'undefined_attr'
Альтернативные инструменты в Python
MagicMock - расширяет Mock, добавляя поддержку магических методов. Подходит для имитации контейнеров, контекстных менеджеров и других объектов с специальными методами.
PropertyMock - специализированный Mock для имитации свойств. Используется с patch.object для замены property объектов.
AsyncMock - предназначен для тестирования асинхронного кода. Поддерживает синтаксис async/await.
patch декораторы и менеджеры контекста - временно заменяют целевые объекты Mock'ами в заданном пространстве имен.
Аналоги в других языках программирования
JavaScript (Jest):
const mockFn = jest.fn();
mockFn.mockReturnValue(42);
console.log(mockFn());42
Java (Mockito):
List mockedList = mock(List.class);
when(mockedList.get(0)).thenReturn('first');
System.out.println(mockedList.get(0));first
PHP (PHPUnit):
$mock = $this->createMock(SomeClass::class);
$mock->method('doSomething')
->willReturn('result');
echo $mock->doSomething();result
Golang (testify/mock):
mockObj := new(MockObject)
mockObj.On('MethodName', args).Return(result, nil)
val, err := mockObj.MethodName(args)val = result, err = nil
Типичные ошибки при использовании
Неправильная настройка side_effect:
mock = Mock(side_effect=ValueError)
# Ошибка: передается класс, а не экземпляр
try:
mock()
except TypeError as e:
print(f'Ошибка: {e}')Ошибка: exceptions must derive from BaseException
Путаница между return_value и side_effect:
mock = Mock(return_value=1, side_effect=[2, 3])
print(mock()) # Используется side_effect, а не return_value2
Использование Mock вместо MagicMock для магических методов:
mock_dict = Mock()
try:
len(mock_dict)
except TypeError as e:
print(f'Ошибка: {e}')Ошибка: object of type 'Mock' has no len()
Изменения в последних версиях
В Python 3.8 добавлен параметр unsafe, который по умолчанию равен False. Это предотвращает случайный доступ к атрибутам, начинающимся с 'assert' или 'assret'.
В Python 3.10 улучшена обработка спецификаций для классов с помощью spec_set, который делает спецификацию строгой.
В Python 3.11 добавлена поддержка протоколов typing для лучшей интеграции со статическими анализаторами типов.
Расширенные примеры применения
Mock с динамическим поведением:
def dynamic_side_effect(*args, **kwargs):
if kwargs.get('mode') == 'add':
return args[0] + args[1]
return None
mock_calc = Mock(side_effect=dynamic_side_effect)
print(mock_calc(5, 3, mode='add'))
print(mock_calc(5, 3))8 None
Верификация вызовов:
mock_service = Mock()
mock_service.process('data1')
mock_service.process('data2')
print(mock_service.process.call_args_list)
print(f'Вызовов: {mock_service.process.call_count}')[call('data1'), call('data2')]
Вызовов: 2Mock с wraps:
real_dict = {'a': 1, 'b': 2}
mock_dict = Mock(wraps=real_dict)
print(mock_dict['a']) # Доступ через __getitem__
print(mock_dict.keys()) # Вызов реального метода1 dict_keys(['a', 'b'])
Цепочка вызовов:
mock = Mock()
mock.connection().cursor().execute.return_value = 'success'
result = mock.connection().cursor().execute('SELECT 1')
print(result)success