Spec-Zone.ru › Python 3.10

unittest.mock — библиотека объектов-моков

Новая в версии 3.3.

Исходный код: Lib/unittest/mock.py

unittest.mock — это библиотека для тестирования на Python. Она позволяет заменить части вашей системы, подлежащей тестированию, на объекты-моки и делать утверждения о том, как они были использованы.

unittest.mock предоставляет базовый класс Mock, устраняя необходимость создавать множество заглушек в вашем наборе тестов. После выполнения действия вы можете сделать утверждения о том, какие методы/атрибуты были использованы и с какими аргументами они были вызваны. Также вы можете указать значения возврата и задать необходимые атрибуты обычным способом.

Кроме того, mock предоставляет декоратор patch(), который обрабатывает подмену атрибутов модуля и класса в рамках теста, а также sentinel для создания уникальных объектов. Обратитесь к краткому руководству для примеров использования Mock, MagicMock и patch().

Mock предназначен для использования с unittest и основан на шаблоне «действие —> утверждение» вместо шаблона «запись —> воспроизведение», используемого многими фреймворками для создания моков.

Существует обратная портативная версия unittest.mock для более ранних версий Python, доступная как mock на PyPI.

Краткое руководство

Mock и MagicMock объекты создают все атрибуты и методы по мере их доступа и сохраняют детали о том, как они были использованы. Вы можете их настроить, чтобы указать значения возврата или ограничить доступные атрибуты, а затем сделать утверждения о том, как они были использованы:

>>> from unittest.mock import MagicMock
>>> thing = ProductionClass()
>>> thing.method = MagicMock(return_value=3)
>>> thing.method(3, 4, 5, key='value')
3
>>> thing.method.assert_called_with(3, 4, 5, key='value')

side_effect позволяет выполнять побочные эффекты, включая возбуждение исключения при вызове мока:

>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()
Traceback (most recent call last):
 ...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect(arg):
...     return values[arg]
...
>>> mock.side_effect = side_effect
>>> mock('a'), mock('b'), mock('c')
(1, 2, 3)
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)

У Mock есть множество других способов настройки и управления его поведением. Например, аргумент spec настраивает мок, чтобы он получал свою спецификацию от другого объекта. Попытка получить доступ к атрибутам или методам мока, которые не существуют в спецификации, приведет к ошибке AttributeError.

Декоратор/менеджер контекста patch() упрощает создание моков для классов или объектов в тестируемом модуле. Объект, который вы укажете, будет заменен моком (или другим объектом) во время теста и восстановлен после его завершения:

>>> from unittest.mock import patch
>>> @patch('module.ClassName2')
... @patch('module.ClassName1')
... def test(MockClass1, MockClass2):
...     module.ClassName1()
...     module.ClassName2()
...     assert MockClass1 is module.ClassName1
...     assert MockClass2 is module.ClassName2
...     assert MockClass1.called
...     assert MockClass2.called
...
>>> test()

Примечание

Когда вы вложены декораторы patch, моки передаются в декорированную функцию в том же порядке, в котором они были применены (обычный порядок Python для применения декораторов). Это означает снизу вверх, поэтому в приведенном выше примере мок для module.ClassName1 передается первым.

С patch() важно, чтобы вы подменяли объекты в пространстве имен, где они ищутся. Обычно это просто, но для быстрого руководства прочтите где подменять.

Помимо декоратора, patch() может быть использован как менеджер контекста в инструкции with:

>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
...     thing = ProductionClass()
...     thing.method(1, 2, 3)
...
>>> mock_method.assert_called_once_with(1, 2, 3)

Также имеется patch.dict() для установки значений в словаре только в определенном объеме и восстановления словаря к его первоначальному состоянию после завершения теста:

>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
...     assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original

Mock поддерживает создание моков для магических методов Python магических методов. Наиболее простой способ использования магических методов — с классом MagicMock. Он позволяет выполнять действия, такие как:

>>> mock = MagicMock()
>>> mock.__str__.return_value = 'foobarbaz'
>>> str(mock)
'foobarbaz'
>>> mock.__str__.assert_called_with()

Mock позволяет назначать функции (или другие экземпляры Mock) магическим методам, и они будут вызываться соответствующим образом. Класс MagicMock — это просто вариант Mock, у которого все магические методы созданы заранее (ну, все полезные).

Следующий пример демонстрирует использование магических методов с обычным классом Mock:

>>> mock = Mock()
>>> mock.__str__ = Mock(return_value='wheeeeee')
>>> str(mock)
'wheeeeee'

Для обеспечения того, чтобы объекты-моки в ваших тестах имели тот же API, что и заменяемые объекты, вы можете использовать авто-спецификацию. Авто-спецификацию можно выполнить с помощью аргумента autospec для patch или функции create_autospec(). Авто-спецификация создает объекты-моки с теми же атрибутами и методами, что и заменяемые объекты, а любые функции и методы (включая конструкторы) имеют ту же сигнатуру вызова, что и реальный объект.

Это гарантирует, что ваши моки будут давать ту же ошибку, что и ваш производственный код, если они будут использованы неправильно:

>>> from unittest.mock import create_autospec
>>> def function(a, b, c):
...     pass
...
>>> mock_function = create_autospec(function, return_value='fishy')
>>> mock_function(1, 2, 3)
'fishy'
>>> mock_function.assert_called_once_with(1, 2, 3)
>>> mock_function('wrong arguments')
Traceback (most recent call last):
 ...
TypeError: <lambda>() takes exactly 3 arguments (1 given)

create_autospec() также может использоваться для классов, где она копирует сигнатуру метода __init__, и для вызываемых объектов, где она копирует сигнатуру метода __call__.

Класс Mock

Mock — гибкий объект-мок, предназначенный для замены использования заглушек и тестовых двойников в вашем коде. Моки вызываемы и создают атрибуты как новые моки при их доступе 1. Обращение к одному и тому же атрибуту всегда возвращает тот же мок. Моки записывают, как вы их используете, что позволяет делать утверждения о том, что ваш код с ними сделал.

MagicMock — это подкласс Mock со всеми магическими методами, предварительно созданными и готовыми к использованию. Также существуют невызываемые варианты, полезные, когда вы создаете моки для объектов, которые не вызываемы: NonCallableMock и NonCallableMagicMock

Декораторы patch() упрощают временную замену классов в конкретном модуле объектом Mock. По умолчанию patch() создает MagicMock для вас. Вы можете указать альтернативный класс Mock с помощью аргумента new_callable к patch().

class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)

Создайте новый объект Mock. Mock принимает несколько необязательных аргументов, которые определяют поведение объекта Mock:

  • spec: Это может быть список строк или существующий объект (класс или экземпляр), который служит спецификацией для объекта mock. Если вы передаёте объект, то список строк формируется путём вызова dir на объекте (исключая неподдерживаемые магические атрибуты и методы). Доступ к любому атрибуту, отсутствующему в этом списке, вызовет AttributeError.

    Если spec является объектом (а не списком строк), то __class__ возвращает класс объекта spec. Это позволяет моделям проходить тесты isinstance().

  • spec_set: Более строгий вариант spec. Если используется, попытка установить или получить атрибут объекта mock, который отсутствует в объекте, переданном как spec_set, вызовет AttributeError.
  • side_effect: Функция, которая вызывается всякий раз, когда вызывается Mock. См. атрибут side_effect. Полезно для создания исключений или динамического изменения возвращаемых значений. Функция вызывается с теми же аргументами, что и mock, и, если она не возвращает DEFAULT, значение, возвращаемое этой функцией, используется как возвращаемое значение.

    В качестве альтернативы, side_effect может быть классом или экземпляром исключения. В этом случае исключение будет возбуждено при вызове mock.

    Если side_effect является итерируемым объектом, каждый вызов mock вернёт следующее значение из итерируемого объекта.

    side_effect можно очистить, установив его в None.

  • return_value: Значение, возвращаемое при вызове mock. По умолчанию это новый Mock (созданный при первом доступе). См. атрибут return_value.
  • unsafe: По умолчанию доступ к любому атрибуту с именем, начинающимся с assert, assret, asert, aseert или assrt, вызовет AttributeError. Передача unsafe=True позволит получить доступ к этим атрибутам.

    Новое в версии 3.5.

  • wraps: Элемент для обертывания объекта mock. Если wraps не None , то вызов Mock передаст вызов обернутому объекту (вернув реальный результат). Обращение к атрибуту mock вернёт объект Mock, который оборачивает соответствующий атрибут обернутого объекта (поэтому попытка доступа к атрибуту, который не существует, вызовет AttributeError).

    Если для mock явно задано return_value, вызовы не передаются обернутому объекту, и вместо этого возвращается return_value.

  • name: Если у mock есть имя, оно будет использовано в представлении mock. Это может быть полезно для отладки. Имя передаётся дочерним моделям.

Mocks также могут быть вызваны с произвольными именованными аргументами. Они будут использованы для установки атрибутов на mock после его создания. См. метод configure_mock() для получения подробностей.

assert_called()

Утверждение, что mock был вызван хотя бы один раз.

>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called()

Новое в версии 3.6.

assert_called_once()

Утверждение, что mock был вызван ровно один раз.

>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_once()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_once()
Traceback (most recent call last):
...
AssertionError: Expected 'method' to have been called once. Called 2 times.

Новое в версии 3.6.

assert_called_with(*args, **kwargs)

Этот метод является удобным способом утверждения, что последний вызов был выполнен определённым образом:

>>> mock = Mock()
>>> mock.method(1, 2, 3, test='wow')
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_with(1, 2, 3, test='wow')
assert_called_once_with(*args, **kwargs)

Утверждение, что mock был вызван ровно один раз и этот вызов был с указанными аргументами.

>>> mock = Mock(return_value=None)
>>> mock('foo', bar='baz')
>>> mock.assert_called_once_with('foo', bar='baz')
>>> mock('other', bar='values')
>>> mock.assert_called_once_with('other', bar='values')
Traceback (most recent call last):
  ...
AssertionError: Expected 'mock' to be called once. Called 2 times.
assert_any_call(*args, **kwargs)

Утверждение, что mock был вызван с указанными аргументами.

Утверждение выполняется, если mock был вызван хоть раз, в отличие от assert_called_with() и assert_called_once_with(), которые выполняются только если вызов является последним, и в случае assert_called_once_with() он также должен быть единственным.

>>> mock = Mock(return_value=None)
>>> mock(1, 2, arg='thing')
>>> mock('some', 'thing', 'else')
>>> mock.assert_any_call(1, 2, arg='thing')
assert_has_calls(calls, any_order=False)

Утверждение, что mock был вызван с указанными вызовами. Проверяется список mock_calls.

Если any_order равно false, вызовы должны быть последовательными. До или после указанных вызовов могут быть дополнительные вызовы.

Если any_order равно true, вызовы могут быть в любом порядке, но все они должны присутствовать в mock_calls.

>>> mock = Mock(return_value=None)
>>> mock(1)
>>> mock(2)
>>> mock(3)
>>> mock(4)
>>> calls = [call(2), call(3)]
>>> mock.assert_has_calls(calls)
>>> calls = [call(4), call(2), call(3)]
>>> mock.assert_has_calls(calls, any_order=True)
assert_not_called()

Утверждение, что mock никогда не был вызван.

>>> m = Mock()
>>> m.hello.assert_not_called()
>>> obj = m.hello()
>>> m.hello.assert_not_called()
Traceback (most recent call last):
  ...
AssertionError: Expected 'hello' to not have been called. Called 1 times.

Новое в версии 3.5.

reset_mock(*, return_value=False, side_effect=False)

Метод reset_mock сбрасывает все атрибуты вызовов объекта mock:

>>> mock = Mock(return_value=None)
>>> mock('hello')
>>> mock.called
True
>>> mock.reset_mock()
>>> mock.called
False

Изменено в версии 3.6: Добавлены два аргумента только для ключевых слов в функцию reset_mock.

Это может быть полезно, когда вы хотите выполнить серию утверждений, которые повторно используют один и тот же объект. Обратите внимание, что reset_mock() не очищает возвращаемое значение, side_effect или любые атрибуты дочерних объектов, которые вы установили с помощью обычного присваивания, по умолчанию. В случае, если вы хотите сбросить return_value или side_effect, передайте соответствующий параметр как True. Дочерние mocks и mock возвращаемого значения (если таковой имеется) также сбрасываются.

Примечание

return_value и side_effect являются аргументами только для ключевых слов.

mock_add_spec(spec, spec_set=False)

Добавление спецификации к mock. spec может быть объектом или списком строк. Только атрибуты в spec могут быть получены как атрибуты из mock.

Если spec_set равно true, то только атрибуты в спецификации могут быть установлены.

attach_mock(mock, attribute)

Прикрепление mock в качестве атрибута к этому, заменяя его имя и родителя. Вызовы прикрепленного mock будут записаны в атрибуты method_calls и mock_calls этого.

configure_mock(**kwargs)

Установка атрибутов mock через именованные аргументы.

Атрибуты, плюс возвращаемые значения и побочные эффекты, могут быть установлены на дочерних моделях с использованием стандартной нотации точки и распаковки словаря в вызове метода:

>>> mock = Mock()
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock.configure_mock(**attrs)
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
  ...
KeyError

То же самое можно сделать в вызове конструктора mocks:

>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
  ...
KeyError

configure_mock() создана для облегчения конфигурации после создания mock.

__dir__()

Объекты Mock ограничивают результаты dir(some_mock) полезными результатами. Для mock с spec это включает все разрешённые атрибуты для mock.

См. FILTER_DIR для того, что делает эта фильтрация и как её отключить.

_get_child_mock(**kw)

Создание дочерних mock для атрибутов и возвращаемого значения. По умолчанию дочерние mocks будут того же типа, что и родитель. Подклассы Mock могут захотеть переопределить это, чтобы настроить способ создания дочерних mocks.

Для невызываемых mock используется вызываемый вариант (а не любой пользовательский подкласс).

called

Булево значение, представляющее, был ли вызван объект mock:

>>> mock = Mock(return_value=None)
>>> mock.called
False
>>> mock()
>>> mock.called
True
END_OF_DOCUMENT_MARKER
call_count

Целое число, показывающее, сколько раз был вызван объект-модель:

>>> mock = Mock(return_value=None)
>>> mock.call_count
0
>>> mock()
>>> mock()
>>> mock.call_count
2
return_value

Установите это значение, чтобы настроить возвращаемое значение при вызове модели:

>>> mock = Mock()
>>> mock.return_value = 'fish'
>>> mock()
'fish'

По умолчанию возвращаемое значение — это объект-модель, который можно настроить обычным способом:

>>> mock = Mock()
>>> mock.return_value.attribute = sentinel.Attribute
>>> mock.return_value()
<Mock name='mock()()' id='...'>
>>> mock.return_value.assert_called_with()

return_value также можно задать в конструкторе:

>>> mock = Mock(return_value=3)
>>> mock.return_value
3
>>> mock()
3
side_effect

Это может быть функция, вызываемая при вызове модели, итерируемый объект или исключение (класс или экземпляр) для повышения.

Если вы передаете функцию, она будет вызываться с теми же аргументами, что и модель, и, если функция не возвращает синглтон DEFAULT, вызов модели вернет то, что вернула функция. Если функция возвращает DEFAULT, то модель вернет свое нормальное значение (из return_value).

Если вы передаете итерируемый объект, он используется для получения итератора, который должен возвращать значение при каждом вызове. Это значение может быть экземпляром исключения, которое нужно поднять, или значением, которое нужно вернуть из вызова модели (DEFAULT обработка аналогична случаю с функцией).

Пример модели, которая генерирует исключение (для тестирования обработки исключений API):

>>> mock = Mock()
>>> mock.side_effect = Exception('Boom!')
>>> mock()
Traceback (most recent call last):
  ...
Exception: Boom!

Использование side_effect для возврата последовательности значений:

>>> mock = Mock()
>>> mock.side_effect = [3, 2, 1]
>>> mock(), mock(), mock()
(3, 2, 1)

Использование вызываемого объекта:

>>> mock = Mock(return_value=3)
>>> def side_effect(*args, **kwargs):
...     return DEFAULT
...
>>> mock.side_effect = side_effect
>>> mock()
3

side_effect можно задать в конструкторе. Вот пример, который добавляет единицу к значению, с которым вызывается модель, и возвращает его:

>>> side_effect = lambda value: value + 1
>>> mock = Mock(side_effect=side_effect)
>>> mock(3)
4
>>> mock(-8)
-7

Установка side_effect в None очищает его:

>>> m = Mock(side_effect=KeyError, return_value=3)
>>> m()
Traceback (most recent call last):
 ...
KeyError
>>> m.side_effect = None
>>> m()
3
call_args

Это либо None (если модель еще не вызывалась), либо аргументы, с которыми модель была последний раз вызвана. Это будет кортеж: первый член, к которому также можно получить доступ через свойство args, — это любые упорядоченные аргументы, с которыми вызывалась модель (или пустой кортеж), а второй член, к которому также можно получить доступ через свойство kwargs, — это любые ключевые аргументы (или пустой словарь).

>>> mock = Mock(return_value=None)
>>> print(mock.call_args)
None
>>> mock()
>>> mock.call_args
call()
>>> mock.call_args == ()
True
>>> mock(3, 4)
>>> mock.call_args
call(3, 4)
>>> mock.call_args == ((3, 4),)
True
>>> mock.call_args.args
(3, 4)
>>> mock.call_args.kwargs
{}
>>> mock(3, 4, 5, key='fish', next='w00t!')
>>> mock.call_args
call(3, 4, 5, key='fish', next='w00t!')
>>> mock.call_args.args
(3, 4, 5)
>>> mock.call_args.kwargs
{'key': 'fish', 'next': 'w00t!'}

call_args, вместе с элементами списков call_args_list, method_calls и mock_calls являются объектами call. Это кортежи, поэтому их можно распаковать, чтобы получить отдельные аргументы и выполнить более сложные проверки. См. вызовы как кортежи.

Изменено в версии 3.8: Добавлены свойства args и kwargs.

call_args_list

Это список всех вызовов объекта-модели в последовательности (длина списка соответствует количеству вызовов). До выполнения каких-либо вызовов это пустой список. Объект call можно использовать для удобного построения списков вызовов для сравнения с call_args_list.

>>> mock = Mock(return_value=None)
>>> mock()
>>> mock(3, 4)
>>> mock(key='fish', next='w00t!')
>>> mock.call_args_list
[call(), call(3, 4), call(key='fish', next='w00t!')]
>>> expected = [(), ((3, 4),), ({'key': 'fish', 'next': 'w00t!'},)]
>>> mock.call_args_list == expected
True

Элементы call_args_list являются объектами call. Их можно распаковать как кортежи, чтобы получить отдельные аргументы. См. вызовы как кортежи.

method_calls

Помимо отслеживания вызовов самих моделей, модели также отслеживают вызовы методов и атрибутов, а также их методов и атрибутов:

>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.property.method.attribute()
<Mock name='mock.property.method.attribute()' id='...'>
>>> mock.method_calls
[call.method(), call.property.method.attribute()]

Элементы method_calls являются объектами call. Их можно распаковать как кортежи, чтобы получить отдельные аргументы. См. вызовы как кортежи.

mock_calls

mock_calls записывает все вызовы объекта-модели, его методы, магические методы и возвращаемые значения моделей.

>>> mock = MagicMock()
>>> result = mock(1, 2, 3)
>>> mock.first(a=3)
<MagicMock name='mock.first()' id='...'>
>>> mock.second()
<MagicMock name='mock.second()' id='...'>
>>> int(mock)
1
>>> result(1)
<MagicMock name='mock()()' id='...'>
>>> expected = [call(1, 2, 3), call.first(a=3), call.second(),
... call.__int__(), call()(1)]
>>> mock.mock_calls == expected
True

Элементы mock_calls являются объектами call. Их можно распаковать как кортежи, чтобы получить отдельные аргументы. См. вызовы как кортежи.

Примечание

Способ записи mock_calls означает, что при вложенных вызовах параметры родительских вызовов не записываются и поэтому всегда будут сравниваться одинаковыми:

>>> mock = MagicMock()
>>> mock.top(a=3).bottom()
<MagicMock name='mock.top().bottom()' id='...'>
>>> mock.mock_calls
[call.top(a=3), call.top().bottom()]
>>> mock.mock_calls[-1] == call.top(a=-1).bottom()
True
__class__

Обычно атрибут __class__ объекта возвращает его тип. Для объекта-модели со spec, __class__ возвращает класс спецификации вместо этого. Это позволяет объектам-моделям проходить тесты isinstance() для объекта, который они заменяют/маскируют:

>>> mock = Mock(spec=3)
>>> isinstance(mock, int)
True

__class__ можно присвоить, это позволяет модели проходить проверку isinstance() без принудительного использования спецификации:

>>> mock = Mock()
>>> mock.__class__ = dict
>>> isinstance(mock, dict)
True
class unittest.mock.NonCallableMock(spec=None, wraps=None, name=None, spec_set=None, **kwargs)

Невызываемый вариант Mock. Параметры конструктора имеют то же значение, что и в Mock, за исключением return_value и side_effect, которые не имеют смысла для невызываемой модели.

Объекты-модели, которые используют класс или экземпляр в качестве spec или spec_set, способны проходить тесты isinstance():

>>> mock = Mock(spec=SomeClass)
>>> isinstance(mock, SomeClass)
True
>>> mock = Mock(spec_set=SomeClass())
>>> isinstance(mock, SomeClass)
True

Классы Mock поддерживают моделирование магических методов. Подробности см. в магические методы.

Классы-модели и декораторы patch() принимают произвольные ключевые аргументы для конфигурации. Для декораторов patch() ключевые аргументы передаются в конструктор создаваемой модели. Ключевые аргументы предназначены для настройки атрибутов модели:

>>> m = MagicMock(attribute=3, other='fish')
>>> m.attribute
3
>>> m.other
'fish'

Возвращаемое значение и побочный эффект дочерних моделей могут быть настроены аналогичным образом с использованием точечной нотации. Так как вы не можете использовать имена с точкой непосредственно в вызове, вам нужно создать словарь и распаковать его с помощью **:

>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
  ...
KeyError

Вызываемая модель, созданная со спецификацией (или спецификацией_набора), проанализирует сигнатуру объекта спецификации при сопоставлении вызовов модели. Таким образом, она может сопоставить фактические аргументы вызова независимо от того, были ли они переданы позиционно или по имени:

>>> def f(a, b, c): pass
...
>>> mock = Mock(spec=f)
>>> mock(1, 2, c=3)
<Mock name='mock()' id='140161580456576'>
>>> mock.assert_called_with(1, 2, 3)
>>> mock.assert_called_with(a=1, b=2, c=3)

Это относится к assert_called_with(), assert_called_once_with(), assert_has_calls() и assert_any_call(). При использовании Автоспецификации это также будет относиться к вызовам методов на объекте-заглушке.

Изменено в версии 3.4: Добавлена интроспекция сигнатуры для специфицированных и автоспецифицированных объектов-заглушек.

class unittest.mock.PropertyMock(*args, **kwargs)

Заглушка, предназначенная для использования в качестве свойства или другого дескриптора в классе. PropertyMock предоставляет методы __get__() и __set__() для указания возвращаемого значения при получении.

Получение экземпляра PropertyMock из объекта вызывает заглушку без аргументов. Установка его вызывает заглушку со значением, которое устанавливается.

>>> class Foo:
...     @property
...     def foo(self):
...         return 'something'
...     @foo.setter
...     def foo(self, value):
...         pass
...
>>> with patch('__main__.Foo.foo', new_callable=PropertyMock) as mock_foo:
...     mock_foo.return_value = 'mockity-mock'
...     this_foo = Foo()
...     print(this_foo.foo)
...     this_foo.foo = 6
...
mockity-mock
>>> mock_foo.mock_calls
[call(), call(6)]

Из-за способа хранения атрибутов заглушек вы не можете напрямую прикрепить PropertyMock к объекту-заглушке. Вместо этого вы можете прикрепить его к объекту типа заглушки:

>>> m = MagicMock()
>>> p = PropertyMock(return_value=3)
>>> type(m).foo = p
>>> m.foo
3
>>> p.assert_called_once_with()
class unittest.mock.AsyncMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)

Асинхронная версия MagicMock. Объект AsyncMock будет вести себя так, что объект распознается как асинхронная функция, а результат вызова — как ожидаемое значение.

>>> mock = AsyncMock()
>>> asyncio.iscoroutinefunction(mock)
True
>>> inspect.isawaitable(mock())  
True

Результатом mock() является асинхронная функция, которая после ожидания будет иметь результат side_effect или return_value:

  • если side_effect является функцией, асинхронная функция вернёт результат этой функции,
  • если side_effect является исключением, асинхронная функция поднимет это исключение,
  • если side_effect является итерируемым объектом, асинхронная функция вернёт следующее значение из этого итерируемого объекта. Однако, если последовательность результатов исчерпана, StopAsyncIteration будет поднято немедленно,
  • если side_effect не определено, асинхронная функция вернёт значение, определённое return_value, то есть по умолчанию асинхронная функция возвращает новый объект AsyncMock.

Установка spec для Mock или MagicMock на асинхронную функцию приведет к возврату объекта сопрограммы после вызова.

>>> async def async_func(): pass
...
>>> mock = MagicMock(async_func)
>>> mock
<MagicMock spec='function' id='...'>
>>> mock()  
<coroutine object AsyncMockMixin._mock_call at ...>

Установка spec для Mock, MagicMock или AsyncMock на класс с асинхронными и синхронными функциями автоматически обнаружит синхронные функции и установит их как MagicMock (если родительская заглушка — AsyncMock или MagicMock) или Mock (если родительская заглушка — Mock). Все асинхронные функции будут AsyncMock.

>>> class ExampleClass:
...     def sync_foo():
...         pass
...     async def async_foo():
...         pass
...
>>> a_mock = AsyncMock(ExampleClass)
>>> a_mock.sync_foo
<MagicMock name='mock.sync_foo' id='...'>
>>> a_mock.async_foo
<AsyncMock name='mock.async_foo' id='...'>
>>> mock = Mock(ExampleClass)
>>> mock.sync_foo
<Mock name='mock.sync_foo' id='...'>
>>> mock.async_foo
<AsyncMock name='mock.async_foo' id='...'>

Новое в версии 3.8.

assert_awaited()

Проверить, что заглушка была ожидания (await) по крайней мере один раз. Обратите внимание, что это отделено от того, был ли вызван объект, ключевое слово await должно быть использовано:

>>> mock = AsyncMock()
>>> async def main(coroutine_mock):
...     await coroutine_mock
...
>>> coroutine_mock = mock()
>>> mock.called
True
>>> mock.assert_awaited()
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited.
>>> asyncio.run(main(coroutine_mock))
>>> mock.assert_awaited()
assert_awaited_once()

Проверить, что заглушка была ожидания (await) ровно один раз.

>>> mock = AsyncMock()
>>> async def main():
...     await mock()
...
>>> asyncio.run(main())
>>> mock.assert_awaited_once()
>>> asyncio.run(main())
>>> mock.method.assert_awaited_once()
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited once. Awaited 2 times.
assert_awaited_with(*args, **kwargs)

Проверить, что последнее ожидание (await) было с указанными аргументами.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_with('foo', bar='bar')
>>> mock.assert_awaited_with('other')
Traceback (most recent call last):
...
AssertionError: expected call not found.
Expected: mock('other')
Actual: mock('foo', bar='bar')
assert_awaited_once_with(*args, **kwargs)

Проверить, что заглушка была ожидания (await) ровно один раз и с указанными аргументами.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_once_with('foo', bar='bar')
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_once_with('foo', bar='bar')
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited once. Awaited 2 times.
assert_any_await(*args, **kwargs)

Проверить, что заглушка когда-либо была ожидания (await) с указанными аргументами.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> asyncio.run(main('hello'))
>>> mock.assert_any_await('foo', bar='bar')
>>> mock.assert_any_await('other')
Traceback (most recent call last):
...
AssertionError: mock('other') await not found
assert_has_awaits(calls, any_order=False)

Проверить, что заглушка была ожидания (await) с указанными вызовами. Список await_args_list проверяется на наличие ожиданий (await).

Если any_order ложно, то ожидания (await) должны быть последовательными. До или после указанных ожиданий (await) могут быть дополнительные вызовы.

Если any_order истинно, то ожидания (await) могут быть в любом порядке, но они все должны появиться в await_args_list.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> calls = [call("foo"), call("bar")]
>>> mock.assert_has_awaits(calls)
Traceback (most recent call last):
...
AssertionError: Awaits not found.
Expected: [call('foo'), call('bar')]
Actual: []
>>> asyncio.run(main('foo'))
>>> asyncio.run(main('bar'))
>>> mock.assert_has_awaits(calls)
assert_not_awaited()

Проверить, что заглушка никогда не была ожидания (await).

>>> mock = AsyncMock()
>>> mock.assert_not_awaited()
reset_mock(*args, **kwargs)

См. Mock.reset_mock(). Также устанавливает await_count в 0, await_args в None и очищает await_args_list.

await_count

Целое число, отслеживающее, сколько раз объект-заглушка был ожидания (await).

>>> mock = AsyncMock()
>>> async def main():
...     await mock()
...
>>> asyncio.run(main())
>>> mock.await_count
1
>>> asyncio.run(main())
>>> mock.await_count
2
await_args

Это либо None (если заглушка не была ожидания (await)), либо аргументы, с которыми заглушка была в последний раз ожидания (await). Работает так же, как Mock.call_args.

>>> mock = AsyncMock()
>>> async def main(*args):
...     await mock(*args)
...
>>> mock.await_args
>>> asyncio.run(main('foo'))
>>> mock.await_args
call('foo')
>>> asyncio.run(main('bar'))
>>> mock.await_args
call('bar')
await_args_list

Это список всех ожиданий (await), сделанных объекту-заглушке в последовательности (длина списка — количество ожиданий (await)). До того, как ожидания (await) были сделаны, это пустой список.

>>> mock = AsyncMock()
>>> async def main(*args):
...     await mock(*args)
...
>>> mock.await_args_list
[]
>>> asyncio.run(main('foo'))
>>> mock.await_args_list
[call('foo')]
>>> asyncio.run(main('bar'))
>>> mock.await_args_list
[call('foo'), call('bar')]

Вызов

Объекты Mock вызываемы. Вызов вернет значение, установленное как атрибут return_value. Значение по умолчанию — новый объект Mock; он создается при первом доступе к значению возврата (явном или при вызове Mock) — но он сохраняется и каждый раз возвращается тот же самый.

Вызовы объекта будут записаны в атрибутах, таких как call_args и call_args_list.

Если атрибут side_effect установлен, то он будет вызван после того, как вызов был записан, поэтому, если side_effect вызывает исключение, вызов всё равно записывается.

Самый простой способ заставить мок вызывать исключение при вызове — сделать атрибут side_effect классом или экземпляром исключения:

>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
  ...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
  ...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]

Если side_effect — это функция, то всё, что возвращает эта функция, и возвращают вызовы мока. Функция side_effect вызывается с теми же аргументами, что и мок. Это позволяет динамически изменять значение возвращаемого вызова, в зависимости от входных данных:

>>> def side_effect(value):
...     return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]

Если вы хотите, чтобы мок всё ещё возвращал значение по умолчанию (новый мок) или любое установленное значение возврата, существуют два способа сделать это. Либо верните mock.return_value изнутри side_effect, либо верните DEFAULT:

>>> m = MagicMock()
>>> def side_effect(*args, **kwargs):
...     return m.return_value
...
>>> m.side_effect = side_effect
>>> m.return_value = 3
>>> m()
3
>>> def side_effect(*args, **kwargs):
...     return DEFAULT
...
>>> m.side_effect = side_effect
>>> m()
3

Чтобы удалить side_effect, и вернуться к стандартному поведению, установите side_effect в None:

>>> m = MagicMock(return_value=6)
>>> def side_effect(*args, **kwargs):
...     return 3
...
>>> m.side_effect = side_effect
>>> m()
3
>>> m.side_effect = None
>>> m()
6

side_effect также может быть любым итерируемым объектом. Повторные вызовы мока будут возвращать значения из итерируемого объекта (пока итерируемый объект не иссякнет, и будет вызвано StopIteration):

>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
  ...
StopIteration

Если какие-либо члены итерируемого объекта являются исключениями, они будут вызваны вместо возврата:

>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (most recent call last):
 ...
ValueError
>>> m()
66

Удаление атрибутов

Объекты Mock создают атрибуты по требованию. Это позволяет им имитировать объекты любого типа.

Возможно, вам нужно, чтобы объект Mock возвращал False при вызове hasattr(), или вызывал исключение AttributeError при получении атрибута. Вы можете сделать это, предоставив объект в качестве spec для мока, но это не всегда удобно.

Вы «блокируете» атрибуты, удаляя их. После удаления доступ к атрибуту вызовет исключение AttributeError.

>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
    ...
AttributeError: f

Имена Mock и атрибут name

Поскольку «имя» является аргументом конструктора Mock, если вы хотите, чтобы ваш объект Mock имел атрибут «имя», вы не можете просто передать его при создании. Есть два варианта. Один вариант — использовать configure_mock():

>>> mock = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'my_name'

Более простой вариант — просто установить атрибут «имя» после создания мока:

>>> mock = MagicMock()
>>> mock.name = "foo"

Присоединение Mock в качестве атрибутов

Когда вы присоединяете мок в качестве атрибута другого мока (или в качестве значения возврата), он становится «дочерним» элементом этого мока. Вызовы к дочернему элементу записываются в атрибутах method_calls и mock_calls родительского элемента. Это полезно для настройки дочерних моков и их присоединения к родительскому элементу, или для присоединения моков к родительскому элементу, который записывает все вызовы дочерних элементов и позволяет делать утверждения об порядке вызовов между моками:

>>> parent = MagicMock()
>>> child1 = MagicMock(return_value=None)
>>> child2 = MagicMock(return_value=None)
>>> parent.child1 = child1
>>> parent.child2 = child2
>>> child1(1)
>>> child2(2)
>>> parent.mock_calls
[call.child1(1), call.child2(2)]

Исключение из этого правила — если у мока есть имя. Это позволяет предотвратить «родительство», если по какой-либо причине вы этого не хотите.

>>> mock = MagicMock()
>>> not_a_child = MagicMock(name='not-a-child')
>>> mock.attribute = not_a_child
>>> mock.attribute()
<MagicMock name='not-a-child()' id='...'>
>>> mock.mock_calls
[]

Моки, созданные для вас функцией patch(), автоматически получают имена. Чтобы присоединить моки с именами к родителю, используйте метод attach_mock():

>>> thing1 = object()
>>> thing2 = object()
>>> parent = MagicMock()
>>> with patch('__main__.thing1', return_value=None) as child1:
...     with patch('__main__.thing2', return_value=None) as child2:
...         parent.attach_mock(child1, 'child1')
...         parent.attach_mock(child2, 'child2')
...         child1('one')
...         child2('two')
...
>>> parent.mock_calls
[call.child1('one'), call.child2('two')]
1

Исключения составляют магические методы и атрибуты (те, что имеют двойные подчеркивания в начале и конце). Mock не создаёт их, а вместо этого вызывает исключение AttributeError. Это происходит потому, что интерпретатор часто неявно запрашивает эти методы и очень сбивается с толку, получая новый объект Mock, когда он ожидает магический метод. Если вам нужна поддержка магических методов, см. магические методы.

Декораторы для замены

Декораторы для замены используются для замены объектов только в рамках области действия функции, которую они декорируют. Они автоматически выполняют отмену замены, даже если возникают исключения. Все эти функции также могут быть использованы в операторах with или в качестве декораторов классов.

patch

Примечание

Ключевой момент — произвести замену в правильном пространстве имен. См. раздел где производить замену.

unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

patch() выступает в роли декоратора функции, декоратора класса или контекстного менеджера. Внутри тела функции или оператора with, target заменяется на new объект. При завершении работы функции/оператора with замена отменяется.

Если new опущен, то target заменяется на AsyncMock, если заменяемый объект — асинхронная функция, или на MagicMock в противном случае. Если patch() используется в качестве декоратора, а new опущен, созданный mock передаётся в качестве дополнительного аргумента декорированной функции. Если patch() используется в качестве контекстного менеджера, созданный mock возвращается контекстным менеджером.

target должен быть строкой в формате 'package.module.ClassName'. target импортируется, и указанный объект заменяется на new объект, поэтому target должен быть импортируем из среды, откуда вы вызываете patch(). Target импортируется при выполнении декорированной функции, а не во время декорации.

Ключевые аргументы spec и spec_set передаются в MagicMock, если patch создаёт его для вас.

Кроме того, вы можете передать spec=True или spec_set=True, что заставляет patch передавать объект, подвергаемый имитации, в качестве объекта spec/spec_set.

new_callable позволяет указать другой класс или вызываемый объект, который будет вызван для создания new объекта. По умолчанию AsyncMock используется для асинхронных функций, а MagicMock — для остальных.

Более мощная форма spec — autospec. Если вы установите autospec=True, то mock будет создан с спецификацией из замещаемого объекта. Все атрибуты mock также будут иметь спецификацию соответствующего атрибута замещаемого объекта. Методы и функции, подвергаемые имитации, будут проверять свои аргументы и генерировать TypeError, если они вызываются с неверной сигнатурой. Для mocks, заменяющих класс, их возвращаемое значение (’instance’) будет иметь ту же спецификацию, что и класс. См. функцию create_autospec() и Автоспецификацию.

Вместо autospec=True можно передать autospec=some_object для использования произвольного объекта в качестве спецификации вместо замещаемого.

По умолчанию patch() не сможет заменить атрибуты, которых не существует. Если вы передадите create=True, а атрибут не существует, patch создаст атрибут для вас при вызове заменённой функции и удалит его после выхода из заменённой функции. Это полезно для написания тестов для атрибутов, которые ваш производственный код создаёт во время выполнения. По умолчанию эта функция выключена, так как может быть опасной. Включив её, вы можете писать проходящие тесты для API, которые на самом деле не существуют!

Примечание

Изменено в версии 3.5: Если вы заменяете встроенные функции в модуле, вам не нужно передавать create=True, она будет добавлена по умолчанию.

Patch может быть использован как декоратор класса TestCase. Он работает, декорируя каждый тестовый метод в классе. Это сокращает объём кода, когда ваши тестовые методы используют общий набор замен. patch() находит тесты, ища имена методов, начинающиеся с patch.TEST_PREFIX. По умолчанию это 'test', что соответствует способу, которым unittest находит тесты. Вы можете указать альтернативный префикс, установив patch.TEST_PREFIX.

Patch может использоваться как контекстный менеджер с оператором with. В этом случае замена применяется к отступом блоку кода после оператора with. Если вы используете "as", то заменённый объект будет связан с именем после "as"; очень полезно, если patch() создаёт для вас объект mock.

patch() принимает произвольные ключевые аргументы. Они передаются в AsyncMock, если заменяемый объект асинхронный, в MagicMock в противном случае или в new_callable, если он указан.

patch.dict(...), patch.multiple(...) и patch.object(...) доступны для альтернативных вариантов использования.

patch() в качестве декоратора функции, создавая для вас mock и передавая его в декорированную функцию:

>>> @patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
...     print(mock_class is SomeClass)
...
>>> function(None)
True

Замена класса подменяет класс на экземпляр MagicMock. Если класс экземпляризуется в тестируемом коде, то будет использоваться return_value mock.

Если класс экземпляризуется несколько раз, можно использовать side_effect для возврата нового mock каждый раз. В качестве альтернативы можно установить return_value на любое нужное значение.

Чтобы настроить значения возврата для методов экземпляров в заменённом классе, необходимо сделать это в return_value. Например:

>>> class Class:
...     def method(self):
...         pass
...
>>> with patch('__main__.Class') as MockClass:
...     instance = MockClass.return_value
...     instance.method.return_value = 'foo'
...     assert Class() is instance
...     assert Class().method() == 'foo'
...

Если вы используете spec или spec_set, и patch() заменяет класс, то значение возврата созданного mock будет иметь ту же спецификацию.

>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()

Аргумент new_callable полезен, когда требуется использовать другой класс вместо стандартного MagicMock для создаваемого mock. Например, если нужен NonCallableMock:

>>> thing = object()
>>> with patch('__main__.thing', new_callable=NonCallableMock) as mock_thing:
...     assert thing is mock_thing
...     thing()
...
Traceback (most recent call last):
  ...
TypeError: 'NonCallableMock' object is not callable

Другим вариантом использования может быть замена объекта экземпляром io.StringIO:

>>> from io import StringIO
>>> def foo():
...     print('Something')
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
...     foo()
...     assert mock_stdout.getvalue() == 'Something\n'
...
>>> test()

Когда patch() создаёт mock для вас, часто первым делом нужно настроить mock. Часть этой настройки может быть сделана в вызове patch. Любые произвольные ключевые слова, переданные в вызов, будут использоваться для установки атрибутов созданного mock:

>>> patcher = patch('__main__.thing', first='one', second='two')
>>> mock_thing = patcher.start()
>>> mock_thing.first
'one'
>>> mock_thing.second
'two'

Помимо атрибутов созданного mock, атрибуты, такие как return_value и side_effect, дочерних mock также могут быть настроены. Они не являются синтаксически правильными для прямого передачи в качестве ключевых аргументов, но словарь с этими ключами всё равно может быть расширен в вызов patch() с помощью **:

>>> config = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> patcher = patch('__main__.thing', **config)
>>> mock_thing = patcher.start()
>>> mock_thing.method()
3
>>> mock_thing.other()
Traceback (most recent call last):
  ...
KeyError

По умолчанию попытка замены функции в модуле (или метода или атрибута в классе), которая не существует, завершится ошибкой AttributeError:

>>> @patch('sys.non_existing_attribute', 42)
... def test():
...     assert sys.non_existing_attribute == 42
...
>>> test()
Traceback (most recent call last):
  ...
AttributeError: <module 'sys' (built-in)> does not have the attribute 'non_existing_attribute'

но добавление create=True в вызов patch() позволит предыдущему примеру работать как ожидается:

>>> @patch('sys.non_existing_attribute', 42, create=True)
... def test(mock_stdout):
...     assert sys.non_existing_attribute == 42
...
>>> test()

Изменено в версии 3.8: patch() теперь возвращает AsyncMock, если target — асинхронная функция.

patch.object

patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

Заменяет указанное свойство (атрибут) объекта (target) на объект-заглушку.

patch.object() может использоваться как декоратор, декоратор класса или как менеджер контекста. Аргументы new, spec, create, spec_set, autospec и new_callable имеют то же значение, что и для patch(). Как и patch(), patch.object() принимает произвольные ключевые аргументы для настройки создаваемого объекта-заглушки.

При использовании в качестве декоратора класса patch.object() учитывает patch.TEST_PREFIX для выбора методов для обертывания.

Вы можете вызвать patch.object() с тремя аргументами или двумя. В случае трёх аргументов: объект, имя атрибута и объект, которым нужно заменить атрибут.

При вызове с двумя аргументами объект замены опускается, и для вас создаётся заглушка, которая передаётся в качестве дополнительного аргумента в декорированную функцию:

>>> @patch.object(SomeClass, 'class_method')
... def test(mock_method):
...     SomeClass.class_method(3)
...     mock_method.assert_called_with(3)
...
>>> test()

spec, create и другие аргументы patch.object() имеют такое же значение, как и для patch().

patch.dict

patch.dict(in_dict, values=(), clear=False, **kwargs)

Заменяет словарь или похожий на словарь объект и восстанавливает исходное состояние словаря после теста.

in_dict может быть словарем или контейнером, подобным отображению. Если это отображение, оно должно по крайней мере поддерживать получение, установку и удаление элементов, а также итерацию по ключам.

in_dict также может быть строкой, указывающей имя словаря, который затем будет получен путем импорта.

values может быть словарем значений для установки в словаре. values также может быть итерируемым объектом (key, value) пар.

Если clear истинно, то словарь будет очищен перед установкой новых значений.

patch.dict() также может быть вызван с произвольными ключевыми аргументами для установки значений в словарь.

Изменено в версии 3.8: patch.dict() теперь возвращает изменённый словарь при использовании как менеджер контекста.

patch.dict() может использоваться как менеджер контекста, декоратор или декоратор класса:

>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
...     assert foo == {'newkey': 'newvalue'}
>>> test()
>>> assert foo == {}

При использовании в качестве декоратора класса patch.dict() учитывает patch.TEST_PREFIX (по умолчанию 'test') для выбора методов для обертывания:

>>> import os
>>> import unittest
>>> from unittest.mock import patch
>>> @patch.dict('os.environ', {'newkey': 'newvalue'})
... class TestSample(unittest.TestCase):
...     def test_sample(self):
...         self.assertEqual(os.environ['newkey'], 'newvalue')

Если вы хотите использовать другой префикс для своего теста, вы можете сообщить об этом патчерам, установив patch.TEST_PREFIX. Подробнее о том, как изменить значение, см. TEST_PREFIX.

patch.dict() можно использовать для добавления элементов в словарь или просто для изменения словаря в ходе теста, гарантируя, что словарь восстановится по завершении теста.

>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
...     assert foo == {'newkey': 'newvalue'}
...     assert patched_foo == {'newkey': 'newvalue'}
...     # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
...     patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
...     print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ

Ключевые слова могут быть использованы в вызове patch.dict() для установки значений в словарь:

>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
...     import mymodule
...     mymodule.function('some', 'args')
...
'fish'

patch.dict() может использоваться с объектами, похожими на словарь, которые на самом деле не являются словарями. Как минимум они должны поддерживать получение, установку, удаление элементов и либо итерацию, либо проверку на принадлежность. Это соответствует магическим методам __getitem__(), __setitem__(), __delitem__() и либо __iter__(), либо __contains__().

>>> class Container:
...     def __init__(self):
...         self.values = {}
...     def __getitem__(self, name):
...         return self.values[name]
...     def __setitem__(self, name, value):
...         self.values[name] = value
...     def __delitem__(self, name):
...         del self.values[name]
...     def __iter__(self):
...         return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
...     assert thing['one'] == 2
...     assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']

patch.multiple

patch.multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

Выполняет несколько замещений в одном вызове. Принимает объект для изменения (либо как объект, либо строку для поиска объекта с помощью импорта) и ключевые аргументы для замещений:

with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'):
    ...

Используйте DEFAULT в качестве значения, если вы хотите, чтобы patch.multiple() создавал заглушки для вас. В этом случае созданные заглушки передаются в декорированную функцию по ключевым словам, а словарь возвращается, когда patch.multiple() используется как менеджер контекста.

patch.multiple() может использоваться как декоратор, декоратор класса или как менеджер контекста. Аргументы spec, spec_set, create, autospec и new_callable имеют то же значение, что и для patch(). Эти аргументы будут применены ко всем замещениям, выполненным patch.multiple().

При использовании в качестве декоратора класса patch.multiple() учитывает patch.TEST_PREFIX для выбора методов для обертывания.

Если вы хотите, чтобы patch.multiple() создавал заглушки для вас, вы можете использовать DEFAULT в качестве значения. Если вы используете patch.multiple() как декоратор, то созданные заглушки передаются в декорированную функцию по ключевым словам.

>>> thing = object()
>>> other = object()

>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
...     assert isinstance(thing, MagicMock)
...     assert isinstance(other, MagicMock)
...
>>> test_function()

patch.multiple() может быть вложен с другими patch декораторами, но аргументы, передаваемые по ключевым словам, должны быть после любых стандартных аргументов, созданных patch():

>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
...     assert 'other' in repr(other)
...     assert 'thing' in repr(thing)
...     assert 'exit' in repr(mock_exit)
...
>>> test_function()

Если patch.multiple() используется как менеджер контекста, возвращаемое значение менеджера контекста — это словарь, где созданные заглушки имеют ключ по имени:

>>> with patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) as values:
...     assert 'other' in repr(values['other'])
...     assert 'thing' in repr(values['thing'])
...     assert values['thing'] is thing
...     assert values['other'] is other
...

методы patch: start и stop

Все патчеры имеют методы start() и stop(). Это упрощает выполнение патчинга в методах setUp или там, где нужно выполнить несколько патчей без вложенных декораторов или операторов with.

Для их использования вызовите patch(), patch.object() или patch.dict() как обычно и сохраните ссылку на возвращённый patcher объект. Затем вы можете вызвать start() для применения патча и stop() для его отмены.

Если вы используете patch() для создания заглушки для вас, она будет возвращена в результате вызова patcher.start.

>>> patcher = patch('package.module.ClassName')
>>> from package import module
>>> original = module.ClassName
>>> new_mock = patcher.start()
>>> assert module.ClassName is not original
>>> assert module.ClassName is new_mock
>>> patcher.stop()
>>> assert module.ClassName is original
>>> assert module.ClassName is not new_mock

Типичный пример использования - для выполнения нескольких патчей в методе setUp класса TestCase:

>>> class MyTest(unittest.TestCase):
...     def setUp(self):
...         self.patcher1 = patch('package.module.Class1')
...         self.patcher2 = patch('package.module.Class2')
...         self.MockClass1 = self.patcher1.start()
...         self.MockClass2 = self.patcher2.start()
...
...     def tearDown(self):
...         self.patcher1.stop()
...         self.patcher2.stop()
...
...     def test_something(self):
...         assert package.module.Class1 is self.MockClass1
...         assert package.module.Class2 is self.MockClass2
...
>>> MyTest('test_something').run()

Внимание

Если вы используете этот метод, вы должны убедиться, что патч «отменён» вызовом stop. Это может быть сложнее, чем вы думаете, потому что если в методе setUp произойдёт исключение, tearDown не будет вызван. unittest.TestCase.addCleanup() упрощает это:

>>> class MyTest(unittest.TestCase):
...     def setUp(self):
...         patcher = patch('package.module.Class')
...         self.MockClass = patcher.start()
...         self.addCleanup(patcher.stop)
...
...     def test_something(self):
...         assert package.module.Class is self.MockClass
...

В качестве дополнительного преимущества вам больше не нужно хранить ссылку на patcher объект.

Также возможно остановить все запущенные патчи с помощью patch.stopall().

patch.stopall()

Остановить все активные патчи. Останавливает только патчи, запущенные с помощью start.

patch builtins

Вы можете заменить встроенные функции в модуле. Следующий пример заменяет встроенную функцию ord():

>>> @patch('__main__.ord')
... def test(mock_ord):
...     mock_ord.return_value = 101
...     print(ord('c'))
...
>>> test()
101

TEST_PREFIX

Все патчеры могут использоваться в качестве декораторов классов. При использовании таким образом они оборачивают каждый тестовый метод в классе. Патчеры распознают методы, начинающиеся с 'test' как тестовые методы. Это тот же способ, что и у unittest.TestLoader по умолчанию для поиска тестовых методов.

Возможно, вам нужно использовать другой префикс для ваших тестов. Вы можете сообщить патчерам об использовании другого префикса, установив patch.TEST_PREFIX:

>>> patch.TEST_PREFIX = 'foo'
>>> value = 3
>>>
>>> @patch('__main__.value', 'not three')
... class Thing:
...     def foo_one(self):
...         print(value)
...     def foo_two(self):
...         print(value)
...
>>>
>>> Thing().foo_one()
not three
>>> Thing().foo_two()
not three
>>> value
3

Вложенные декораторы патчинга

Если вам нужно выполнить несколько патчей, вы можете просто указать их последовательно.

Вы можете объединить несколько декораторов патчинга, используя этот шаблон:

>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
...     assert SomeClass.static_method is mock1
...     assert SomeClass.class_method is mock2
...     SomeClass.static_method('foo')
...     SomeClass.class_method('bar')
...     return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')

Обратите внимание, что декораторы применяются снизу вверх. Это стандартный способ применения декораторов в Python. Порядок созданных mocks, передаваемых в вашу тестовую функцию, соответствует этому порядку.

Где производить патчинг

patch() работает, временно изменяя объект, на который указывает имя, на другой. Может быть много имен, указывающих на любой отдельный объект, поэтому для работы патчинга необходимо убедиться, что вы патчите имя, используемое тестируемой системой.

Основной принцип заключается в том, что вы патчите место, где ищется объект, что необязательно совпадает с местом его определения. Несколько примеров помогут прояснить это.

Представьте себе проект, который мы хотим протестировать со следующей структурой:

a.py
    -> Defines SomeClass

b.py
    -> from a import SomeClass
    -> some_function instantiates SomeClass

Теперь мы хотим протестировать some_function, но хотим смоделировать SomeClass с помощью patch(). Проблема в том, что при импорте модуля b (который нам потребуется), он импортирует SomeClass из модуля a. Если мы используем patch() для моделирования a.SomeClass , то это не повлияет на наш тест; модуль b уже имеет ссылку на реальный SomeClass , и кажется, что наш патч не сработал.

Ключ в том, чтобы патчить SomeClass там, где он используется (или где его ищут). В этом случае some_function фактически ищет SomeClass в модуле b, где мы его импортировали. Патчинг должен выглядеть так:

@patch('b.SomeClass')

Однако рассмотрим альтернативный сценарий, где вместо from a import SomeClass модуль b выполняет import a , а some_function использует a.SomeClass. Обе эти формы импорта распространены. В этом случае класс, который мы хотим патчить, ищется в модуле, поэтому нам нужно патчить a.SomeClass вместо этого:

@patch('a.SomeClass')

Патчинг дескрипторов и прокси-объектов

И patch, и patch.object корректно патчат и восстанавливают дескрипторы: методы класса, статические методы и свойства. Вы должны патчить их на классе, а не на экземпляре. Они также работают с некоторыми объектами, которые проксируют доступ к атрибутам, например, объектом настроек django.

Поддержка MagicMock и магических методов

Моделирование магических методов

Mock поддерживает моделирование методов протокола Python, также известных как «магические методы». Это позволяет объектам-моделям заменять контейнеры или другие объекты, реализующие протоколы Python.

Поскольку магические методы ищутся по-другому, чем обычные методы 2, эта поддержка была реализована специально. Это означает, что поддерживаются только определенные магические методы. Список поддерживаемых методов включает почти все из них. Если каких-то не хватает, сообщите нам, пожалуйста.

Вы моделируете магические методы, назначив интересующий метод функции или экземпляру модели. Если вы используете функцию, она должна принимать self в качестве первого аргумента 3.

>>> def __str__(self):
...     return 'fooble'
...
>>> mock = Mock()
>>> mock.__str__ = __str__
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__str__ = Mock()
>>> mock.__str__.return_value = 'fooble'
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__iter__ = Mock(return_value=iter([]))
>>> list(mock)
[]

Один из вариантов использования — моделирование объектов, используемых в качестве менеджеров контекста в операторе with:

>>> mock = Mock()
>>> mock.__enter__ = Mock(return_value='foo')
>>> mock.__exit__ = Mock(return_value=False)
>>> with mock as m:
...     assert m == 'foo'
...
>>> mock.__enter__.assert_called_with()
>>> mock.__exit__.assert_called_with(None, None, None)

Вызовы магических методов не отображаются в method_calls, но записываются в mock_calls.

Примечание

Если вы используете ключевой аргумент spec для создания модели, то попытка установить магический метод, которого нет в спецификации, вызовет AttributeError.

Полный список поддерживаемых магических методов:

  • __hash__, __sizeof__, __repr__ и __str__
  • __dir__, __format__ и __subclasses__
  • __round__, __floor__, __trunc__ и __ceil__
  • Сравнения: __lt__, __gt__, __le__, __ge__, __eq__ и __ne__
  • Методы контейнеров: __getitem__, __setitem__, __delitem__, __contains__, __len__, __iter__, __reversed__ и __missing__
  • Менеджер контекста: __enter__, __exit__, __aenter__ и __aexit__
  • Унарные числовые методы: __neg__, __pos__ и __invert__
  • Числовые методы (включая варианты правой части и на месте): __add__, __sub__, __mul__, __matmul__, __div__, __truediv__, __floordiv__, __mod__, __divmod__, __lshift__, __rshift__, __and__, __xor__, __or__, и __pow__
  • Методы преобразования числовых типов: __complex__, __int__, __float__ и __index__
  • Методы дескриптора: __get__, __set__ и __delete__
  • Запись в pickle: __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate__ и __setstate__
  • Представление пути файловой системы: __fspath__
  • Методы асинхронной итерации: __aiter__ и __anext__

Изменено в версии 3.8: Добавлена поддержка os.PathLike.__fspath__().

Изменено в версии 3.8: Добавлена поддержка __aenter__, __aexit__, __aiter__ и __anext__.

Следующие методы существуют, но не поддерживаются, так как они используются mock, не могут быть динамически установлены или могут вызывать проблемы:

  • __getattr__, __setattr__, __init__ и __new__
  • __prepare__, __instancecheck__, __subclasscheck__, __del__

Магическая модель

Существует два MagicMock варианта: MagicMock и NonCallableMagicMock.

class unittest.mock.MagicMock(*args, **kw)

MagicMock — это подкласс Mock с реализациями по умолчанию большинства магических методов. Вы можете использовать MagicMock без необходимости самостоятельной настройки магических методов.

Параметры конструктора имеют то же значение, что и для Mock.

Если вы используете аргументы spec или spec_set, то создадутся только магические методы, присутствующие в спецификации.

class unittest.mock.NonCallableMagicMock(*args, **kw)

Не вызываемый вариант MagicMock.

Параметры конструктора имеют то же значение, что и для MagicMock, за исключением return_value и side_effect, которые не имеют значения для невызываемой модели.

Магические методы настраиваются с помощью объектов MagicMock, поэтому вы можете их настроить и использовать обычным способом:

>>> mock = MagicMock()
>>> mock[3] = 'fish'
>>> mock.__setitem__.assert_called_with(3, 'fish')
>>> mock.__getitem__.return_value = 'result'
>>> mock[2]
'result'

По умолчанию многие методы протокола должны возвращать объекты определенного типа. Эти методы предварительно настроены с возвращаемым значением по умолчанию, чтобы их можно было использовать, не делая ничего, если вам не нужно возвращаемое значение. Вы все равно можете установить возвращаемое значение вручную, если хотите изменить значение по умолчанию.

Методы и их значения по умолчанию:

  • __lt__: NotImplemented
  • __gt__: NotImplemented
  • __le__: NotImplemented
  • __ge__: NotImplemented
  • __int__: 1
  • __contains__: False
  • __len__: 0
  • __iter__: iter([])
  • __exit__: False
  • __aexit__: False
  • __complex__: 1j
  • __float__: 1.0
  • __bool__: True
  • __index__: 1
  • __hash__: хэш по умолчанию для модели
  • __str__: строка по умолчанию для модели
  • __sizeof__: размер по умолчанию для модели

Например:

>>> mock = MagicMock()
>>> int(mock)
1
>>> len(mock)
0
>>> list(mock)
[]
>>> object() in mock
False

Два метода равенства, __eq__() и __ne__(), являются специальными. Они выполняют сравнение по идентичности по умолчанию, используя атрибут side_effect, если вы не измените их возвращаемое значение на что-то другое:

>>> MagicMock() == 3
False
>>> MagicMock() != 3
True
>>> mock = MagicMock()
>>> mock.__eq__.return_value = True
>>> mock == 3
True

Возвращаемое значение MagicMock.__iter__() может быть любым итерируемым объектом и не обязательно итератором:

>>> mock = MagicMock()
>>> mock.__iter__.return_value = ['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']

Если возвращаемое значение является итератором, то итерация по нему один раз его израсходует, и последующие итерации вернут пустой список:

>>> mock.__iter__.return_value = iter(['a', 'b', 'c'])
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
[]

MagicMock имеет все поддерживаемые магические методы, настроенные, за исключением некоторых устаревших и малоизвестных. Вы все равно можете настроить их по желанию.

Магические методы, которые поддерживаются, но по умолчанию не настроены в MagicMock, это:

  • __subclasses__
  • __dir__
  • __format__
  • __get__, __set__ и __delete__
  • __reversed__ и __missing__
  • __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate__ и __setstate__
  • __getformat__ и __setformat__
2

Магические методы должны ищятся в классе, а не в экземпляре. Разные версии Python несогласованно применяют это правило. Поддерживаемые методы протокола должны работать со всеми поддерживаемыми версиями Python.

3

Функция в основном подключена к классу, но каждый Mock экземпляр изолирован от остальных.

Справочные средства

sentinel

unittest.mock.sentinel

Объект sentinel предоставляет удобный способ предоставления уникальных объектов для ваших тестов.

Атрибуты создаются по запросу, когда вы обращаетесь к ним по имени. Обращение к одному и тому же атрибуту всегда возвращает один и тот же объект. Возвращаемые объекты имеют разумное представление, чтобы сообщения об ошибках тестов были удобочитаемыми.

Изменено в версии 3.7: Атрибуты sentinel теперь сохраняют свою идентичность при copied или pickled.

Иногда при тестировании вам нужно проверить, что определённый объект передаётся в качестве аргумента другому методу или возвращается. Часто для этого создаются именованные объекты-маяки. sentinel предоставляет удобный способ создания и проверки идентичности таких объектов.

В этом примере мы подменяем method для возврата sentinel.some_object:

>>> real = ProductionClass()
>>> real.method = Mock(name="method")
>>> real.method.return_value = sentinel.some_object
>>> result = real.method()
>>> assert result is sentinel.some_object
>>> result
sentinel.some_object

DEFAULT

unittest.mock.DEFAULT

Объект DEFAULT — это предварительно созданный маяк (на самом деле sentinel.DEFAULT). Он может использоваться функциями side_effect для указания того, что необходимо использовать обычное возвращаемое значение.

call

unittest.mock.call(*args, **kwargs)

call() — вспомогательный объект для упрощения утверждений, для сравнения с call_args, call_args_list, mock_calls и method_calls. call() также можно использовать с assert_has_calls().

>>> m = MagicMock(return_value=None)
>>> m(1, 2, a='foo', b='bar')
>>> m()
>>> m.call_args_list == [call(1, 2, a='foo', b='bar'), call()]
True
call.call_list()

Для объекта вызова, представляющего несколько вызовов, call_list() возвращает список всех промежуточных вызовов, а также конечного вызова.

call_list особенно полезен для составления утверждений по «цепочке вызовов». Цепочка вызовов — это несколько вызовов на одной строке кода. Это приводит к нескольким записям в mock_calls на макете. Ручное построение последовательности вызовов может быть утомительным.

call_list() может построить последовательность вызовов из одной и той же цепочки вызовов:

>>> m = MagicMock()
>>> m(1).method(arg='foo').other('bar')(2.0)
<MagicMock name='mock().method().other()()' id='...'>
>>> kall = call(1).method(arg='foo').other('bar')(2.0)
>>> kall.call_list()
[call(1),
 call().method(arg='foo'),
 call().method().other('bar'),
 call().method().other()(2.0)]
>>> m.mock_calls == kall.call_list()
True

Объект call представляет собой кортеж из (позиционных аргументов, аргументов ключевых слов) или (имя, позиционные аргументы, аргументы ключевых слов), в зависимости от способа его построения. При самостоятельном построении это не особенно интересно, но объекты call в атрибутах Mock.call_args, Mock.call_args_list и Mock.mock_calls можно проанализировать, чтобы получить отдельные аргументы, которые они содержат.

Объекты call в атрибутах Mock.call_args и Mock.call_args_list являются двумерными кортежами из (позиционные аргументы, аргументы ключевых слов), тогда как объекты call в атрибуте Mock.mock_calls, наряду с теми, что вы создаёте сами, являются трёхмерными кортежами из (имя, позиционные аргументы, аргументы ключевых слов).

Вы можете использовать их «кортежность», чтобы извлечь отдельные аргументы для более сложного анализа и утверждений. Позиционные аргументы — это кортеж (пустой кортеж, если нет позиционных аргументов), а аргументы ключевых слов — словарь:

>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> kall.args
(1, 2, 3)
>>> kall.kwargs
{'arg': 'one', 'arg2': 'two'}
>>> kall.args is kall[0]
True
>>> kall.kwargs is kall[1]
True
>>> m = MagicMock()
>>> m.foo(4, 5, 6, arg='two', arg2='three')
<MagicMock name='mock.foo()' id='...'>
>>> kall = m.mock_calls[0]
>>> name, args, kwargs = kall
>>> name
'foo'
>>> args
(4, 5, 6)
>>> kwargs
{'arg': 'two', 'arg2': 'three'}
>>> name is m.mock_calls[0][0]
True

create_autospec

unittest.mock.create_autospec(spec, spec_set=False, instance=False, **kwargs)

Создаёт объект макета, используя другой объект в качестве спецификации. Атрибуты макета будут использовать соответствующий атрибут объекта spec в качестве своей спецификации.

Функции или методы, которые имитируются, будут проверять свои аргументы, чтобы убедиться, что они вызываются с правильной подписью.

Если spec_set равно True , попытка установить атрибуты, которые не существуют в объекте спецификации, вызовет AttributeError.

Если в качестве спецификации используется класс, то возвращаемое значение макета (экземпляр класса) будет иметь ту же спецификацию. Вы можете использовать класс как спецификацию для экземпляра объекта, передавая instance=True. Возвращаемый макет будет вызываемым только в том случае, если экземпляры макета вызываемы.

create_autospec() также принимает произвольные ключевые аргументы, которые передаются в конструктор созданного макета.

См. Автоспецификация для примеров использования автоспецификации с create_autospec() и аргументом autospec к patch().

Изменено в версии 3.8: create_autospec() теперь возвращает AsyncMock, если целевой объект — асинхронная функция.

ANY

unittest.mock.ANY

Иногда вам может потребоваться сделать утверждения о некоторых аргументах в вызове макета, но либо не заботиться о некоторых аргументах, либо извлечь их индивидуально из call_args и сделать более сложные утверждения о них.

Чтобы проигнорировать определённые аргументы, вы можете передать объекты, которые сравниваются со всем. Вызовы assert_called_with() и assert_called_once_with() затем завершатся успешно независимо от того, что было передано.

>>> mock = Mock(return_value=None)
>>> mock('foo', bar=object())
>>> mock.assert_called_once_with('foo', bar=ANY)

ANY также может использоваться в сравнениях с списками вызовов, такими как mock_calls:

>>> m = MagicMock(return_value=None)
>>> m(1)
>>> m(1, 2)
>>> m(object())
>>> m.mock_calls == [call(1), call(1, 2), ANY]
True

FILTER_DIR

unittest.mock.FILTER_DIR

FILTER_DIR — это переменная уровня модуля, которая контролирует, как объекты макета реагируют на dir() (только для Python 2.6 и более поздних версий). Значение по умолчанию — True, которое использует фильтрацию, описанную ниже, для отображения только полезных членов. Если вам не нравится эта фильтрация или вам нужно отключить её для диагностических целей, установите mock.FILTER_DIR = False.

При включённой фильтрации dir(some_mock) отображает только полезные атрибуты и включает любые динамически созданные атрибуты, которые обычно не отображаются. Если макет был создан со spec (или, конечно, autospec), то отображаются все атрибуты из оригинала, даже если они ещё не использовались:

>>> dir(Mock())
['assert_any_call',
 'assert_called',
 'assert_called_once',
 'assert_called_once_with',
 'assert_called_with',
 'assert_has_calls',
 'assert_not_called',
 'attach_mock',
 ...
>>> from urllib import request
>>> dir(Mock(spec=request))
['AbstractBasicAuthHandler',
 'AbstractDigestAuthHandler',
 'AbstractHTTPHandler',
 'BaseHandler',
 ...

Многие не очень полезные (частные для Mock, а не для имитируемого объекта) атрибуты с префиксом подчёркивания и двойного подчёркивания были отфильтрованы из результата вызова dir() для Mock. Если вам не нравится это поведение, вы можете отключить его, установив переключатель уровня модуля FILTER_DIR:

>>> from unittest import mock
>>> mock.FILTER_DIR = False
>>> dir(mock.Mock())
['_NonCallableMock__get_return_value',
 '_NonCallableMock__get_side_effect',
 '_NonCallableMock__return_value_doc',
 '_NonCallableMock__set_return_value',
 '_NonCallableMock__set_side_effect',
 '__call__',
 '__class__',
 ...

В качестве альтернативы вы можете просто использовать vars(my_mock) (члены экземпляра) и dir(type(my_mock)) (члены типа), чтобы обойти фильтрацию независимо от mock.FILTER_DIR.

mock_open

unittest.mock.mock_open(mock=None, read_data=None)

Вспомогательная функция для создания мока для замены использования open(). Она работает для open(), вызываемого напрямую или используемого в качестве менеджера контекста.

Аргумент mock — это объект мока, который нужно настроить. Если None (по умолчанию), то для вас будет создан MagicMock с ограниченным API методами или атрибутами, доступными в стандартных файловых потоках.

read_data — это строка для read(), readline() и readlines() методов файлового потока для возвращения. Вызовы этих методов будут брать данные из read_data до тех пор, пока он не будет исчерпан. Мок этих методов довольно прост: каждый раз при вызове mock, read_data перематывается в начало. Если вам нужен больший контроль над данными, которые вы передаёте в тестируемый код, вам нужно будет настроить этот мок самостоятельно. В случаях, когда этого недостаточно, пакеты in-memory файловых систем на PyPI могут предложить реалистичную файловую систему для тестирования.

Изменено в версии 3.4: Добавлена поддержка readline() и readlines(). Мок read() был изменён на потребление read_data вместо возвращения его при каждом вызове.

Изменено в версии 3.5: read_data теперь сбрасывается при каждом вызове mock.

Изменено в версии 3.8: В реализацию добавлена поддержка итераций (например, в циклах for), чтобы корректно потреблять read_data.

Использование open() в качестве менеджера контекста — отличный способ обеспечить правильное закрытие файловых потоков, и это становится распространённым способом:

with open('/some/path', 'w') as f:
    f.write('something')

Проблема в том, что даже если вы смоделируете вызов open(), именно возвращённый объект используется как менеджер контекста (и в нём вызываются __enter__() и __exit__()).

Моделирование менеджеров контекста с помощью MagicMock достаточно распространено и достаточно сложно, поэтому вспомогательная функция полезна.

>>> m = mock_open()
>>> with patch('__main__.open', m):
...     with open('foo', 'w') as h:
...         h.write('some stuff')
...
>>> m.mock_calls
[call('foo', 'w'),
 call().__enter__(),
 call().write('some stuff'),
 call().__exit__(None, None, None)]
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')

И для чтения файлов:

>>> with patch('__main__.open', mock_open(read_data='bibble')) as m:
...     with open('foo') as h:
...         result = h.read()
...
>>> m.assert_called_once_with('foo')
>>> assert result == 'bibble'

Автоспецификация

Автоспецификация основана на существующей spec функции модуля mock. Она ограничивает API моков API исходного объекта (спецификации), но является рекурсивной (реализуется лениво), поэтому атрибуты моков имеют тот же API, что и атрибуты спецификации. Кроме того, смоделированные функции/методы имеют такую же сигнатуру вызова, что и оригинальные, поэтому они генерируют TypeError, если вызываются неправильно.

Прежде чем объяснить, как работает автоспецификация, вот почему она нужна.

Mock — это очень мощный и гибкий объект, но он страдает двумя недостатками при использовании для имитации объектов из системы, подвергаемой тестированию. Один из этих недостатков специфичен для API Mock, а другой — более общая проблема при использовании объектов-моков.

Сначала проблема, специфичная для Mock. Mock имеет два метода assert, которые очень удобны: assert_called_with() и assert_called_once_with().

>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
Traceback (most recent call last):
 ...
AssertionError: Expected 'mock' to be called once. Called 2 times.

Поскольку моки автоматически создают атрибуты по требованию и позволяют вызывать их с произвольными аргументами, если вы неправильно напишите один из этих методов assert, то ваше утверждение исчезнет:

>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assret_called_once_with(4, 5, 6)  # Intentional typo!

Ваши тесты могут пройти молча и неправильно из-за опечатки.

Вторая проблема более общая для имитации. Если вы перефакторили часть своего кода, переименовали члены и т. д., любые тесты для кода, который по-прежнему использует старый API, но использует моки вместо реальных объектов, все равно пройдут. Это означает, что все ваши тесты могут пройти, даже если ваш код сломан.

Обратите внимание, что это еще одна причина, по которой вам нужны интеграционные тесты наряду с модульными. Тестирование всего изолированно — это хорошо, но если вы не тестируете, как ваши модули «связаны вместе», то есть еще много места для ошибок, которые могли бы быть пойманы тестами.

mock уже предоставляет функцию, помогающую в этом, называемую спецификацией. Если вы используете класс или экземпляр в качестве spec для мока, то вы можете получить доступ только к атрибутам мока, которые существуют в реальном классе:

>>> from urllib import request
>>> mock = Mock(spec=request.Request)
>>> mock.assret_called_with  # Intentional typo!
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'assret_called_with'

Спецификация относится только к самому моку, поэтому у нас по-прежнему есть та же проблема с любыми методами мока:

>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with()  # Intentional typo!

Автоспецификация решает эту проблему. Вы можете либо передать autospec=True в patch() / patch.object(), или использовать функцию create_autospec() для создания мока со спецификацией. Если вы используете аргумент autospec=True в patch(), то объект, который заменяется, будет использоваться как объект спецификации. Поскольку спецификация выполняется «лениво» (спецификация создается по мере доступа к атрибутам мока), вы можете использовать ее с очень сложными или глубоко вложенными объектами (например, модулями, которые импортируют модули, которые импортируют модули), без большой потери производительности.

Вот пример использования:

>>> from urllib import request
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> request is mock_request
True
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>

Вы можете видеть, что request.Request имеет спецификацию. request.Request принимает два аргумента в конструкторе (один из которых — self). Вот что происходит, если мы попытаемся вызвать его неправильно:

>>> req = request.Request()
Traceback (most recent call last):
 ...
TypeError: <lambda>() takes at least 2 arguments (1 given)

Спецификация также применяется к экземплярам классов (т. е. возвращаемому значению смоделированных моков):

>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>

Объекты Request не вызываемы, поэтому возвращаемое значение создания экземпляра нашего смоделированного request.Request — это невызываемый мок. С установленной спецификацией любые опечатки в наших утверждениях сгенерируют правильную ошибку:

>>> req.add_header('spam', 'eggs')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_with  # Intentional typo!
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')

Во многих случаях вам просто нужно добавить autospec=True к вашим существующим вызовам patch(), чтобы защититься от ошибок из-за опечаток и изменений API.

Помимо использования autospec через patch(), существует create_autospec() для создания моков с автоспецификацией напрямую:

>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>

Однако это не лишено недостатков и ограничений, поэтому это не поведение по умолчанию. Для того, чтобы знать, какие атрибуты доступны в объекте спецификации, автоспецификация должна интроспективно исследовать (получить доступ к атрибутам) спецификации. При переходе по атрибутам мока происходит соответствующий переход по исходному объекту в скрытом виде. Если у ваших объектов-спецификаций есть свойства или дескрипторы, которые могут вызвать выполнение кода, то вы можете не иметь возможности использовать автоспецификацию. С другой стороны, намного лучше проектировать свои объекты таким образом, чтобы интроспекция была безопасной 4.

Более серьезная проблема заключается в том, что часто атрибуты экземпляров создаются в методе __init__() и вообще не существуют в классе. autospec не может знать о динамически созданных атрибутах и ограничивает API видимыми атрибутами.

>>> class Something:
...   def __init__(self):
...     self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
...   thing = Something()
...   thing.a
...
Traceback (most recent call last):
  ...
AttributeError: Mock object has no attribute 'a'

Существует несколько способов решения этой проблемы. Самый простой, но не обязательно наименее раздражающий способ — просто установить необходимые атрибуты в моке после его создания. Просто потому, что autospec не позволяет получить доступ к атрибутам, которые не существуют в спецификации, это не препятствует их установке:

>>> with patch('__main__.Something', autospec=True):
...   thing = Something()
...   thing.a = 33
...

Существует более агрессивная версия как spec, так и autospec, которая препятствует установке несуществующих атрибутов. Это полезно, если вы хотите убедиться, что ваш код устанавливает только допустимые атрибуты, но, очевидно, это предотвращает эту конкретную ситуацию:

>>> with patch('__main__.Something', autospec=True, spec_set=True):
...   thing = Something()
...   thing.a = 33
...
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'a'

Вероятно, лучший способ решения проблемы — добавить атрибуты класса в качестве значений по умолчанию для членов экземпляра, инициализированных в __init__(). Обратите внимание, что если вы устанавливаете только атрибуты по умолчанию в __init__(), то предоставление их через атрибуты класса (разделяемые, разумеется, между экземплярами) также быстрее. например:

class Something:
    a = 33

Это поднимает еще одну проблему. Довольно часто предоставляется значение по умолчанию None для членов, которые впоследствии станут объектом другого типа. None бесполезен в качестве спецификации, потому что он не позволит вам получить доступ к каким-либо атрибутам или методам. Поскольку None никогда не будет полезен в качестве спецификации и, вероятно, указывает на член, который обычно будет другого типа, autospec не использует спецификацию для членов, которые установлены в None. Они будут просто обычными моками (ну — MagicMocks):

>>> class Something:
...     member = None
...
>>> mock = create_autospec(Something)
>>> mock.member.foo.bar.baz()
<MagicMock name='mock.member.foo.bar.baz()' id='...'>

Если изменение ваших производственных классов для добавления значений по умолчанию вам не подходит, есть больше вариантов. Один из них — просто использовать экземпляр в качестве спецификации вместо класса. Другой — создать подкласс производственного класса и добавить значения по умолчанию в подкласс без влияния на производственный класс. В обоих случаях вам необходимо использовать альтернативный объект в качестве спецификации. К счастью, patch() поддерживает это — вы можете просто передать альтернативный объект в качестве аргумента autospec:

>>> class Something:
...   def __init__(self):
...     self.a = 33
...
>>> class SomethingForTest(Something):
...   a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>
4

Это относится только к классам или уже созданным объектам. Вызов смоделированного класса для создания экземпляра мока не создает реальный экземпляр. Только поиск атрибутов — наряду с вызовами dir() — выполняются.

Запечатывание моков

unittest.mock.seal(mock)

Запечатывание отключит автоматическое создание моков при обращении к атрибуту запечатываемого мока или любому его атрибуту, который уже является моком рекурсивно.

Если экземпляр мока с именем или спецификацией присваивается атрибуту, он не будет рассматриваться в цепочке запечатывания. Это позволяет предотвратить исправление части объекта мока запечатыванием.

>>> mock = Mock()
>>> mock.submock.attribute1 = 2
>>> mock.not_submock = mock.Mock(name="sample_name")
>>> seal(mock)
>>> mock.new_attribute  # This will raise AttributeError.
>>> mock.submock.attribute2  # This will raise AttributeError.
>>> mock.not_submock.attribute2  # This won't raise.

Новое в версии 3.7.

© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/library/unittest.mock.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API