Unittest.mock.Mock: примеры (PYTHON)

Примеры работы с 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_value
2

Использование 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 с динамическим поведением:

Пример python
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

Верификация вызовов:

Пример python
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')]
Вызовов: 2

Mock с wraps:

Пример python
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'])

Цепочка вызовов:

Пример python
mock = Mock()
mock.connection().cursor().execute.return_value = 'success'
result = mock.connection().cursor().execute('SELECT 1')
print(result)
success

питон unittest.mock.Mock function comments

En
Unittest.mock.Mock Create mock object