Spec-Zone.ru › Python 3.12

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 позволяет выполнять побочные эффекты, включая выброс исключения при вызове объекта-заглушки:

>>> from unittest.mock import Mock
>>> 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. Если используется, попытка установить или получить атрибут в моке, который не присутствует в объекте, переданном как spec_set, вызовет AttributeError.
  • side_effect: Функция, вызываемая всякий раз, когда вызывается Mock. См. атрибут side_effect. Полезно для поднятия исключений или динамического изменения возвращаемых значений. Функция вызывается с теми же аргументами, что и Mock, и, если она не возвращает DEFAULT, возвращаемое значение этой функции используется как возвращаемое значение.

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

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

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

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

    Добавлена в версии 3.5.

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

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

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

Моки также могут вызываться с произвольными именованными аргументами. Эти аргументы будут использоваться для установки атрибутов в моке после его создания. Подробности см. в методе configure_mock().

assert_called()

Проверить, что мок был вызван хотя бы один раз.

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

Добавлена в версии 3.6.

assert_called_once()

Проверить, что мок был вызван ровно один раз.

>>> 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(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)

Проверить, что мок был вызван с указанными аргументами.

Проверка пройдёт, если мок был вызван хоть один раз, в отличие от 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_calls.

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

Если any_order истинно, то вызовы могут быть в любом порядке, но все они должны присутствовать в 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()

Проверить, что мок не был вызван ни разу.

>>> 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(return_value=None)
>>> mock('hello')
>>> mock.called
True
>>> mock.reset_mock()
>>> mock.called
False

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

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

Примечание

return_value и side_effect являются именованными аргументами.

mock_add_spec(spec, spec_set=False)

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

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

attach_mock(mock, attribute)

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

configure_mock(**kwargs)

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

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

>>> 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

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

>>> 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() существует, чтобы упростить конфигурацию после создания мока.

__dir__()

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

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

_get_child_mock(**kw)

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

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

called

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

>>> mock = Mock(return_value=None)
>>> mock.called
False
>>> mock()
>>> mock.called
True
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

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

>>> 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)

Заглушка, предназначенная для использования в качестве свойства (property) или другого дескриптора в классе. 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()

Внимание

Если AttributeError возникает в PropertyMock, он будет интерпретироваться как недостающий дескриптор и будет вызван __getattr__() на родительской заглушке:

>>> m = MagicMock()
>>> no_attribute = PropertyMock(side_effect=AttributeError)
>>> type(m).my_property = no_attribute
>>> m.my_property
<MagicMock name='mock.my_property' id='140165240345424'>

См. __getattr__() для получения подробностей.

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:

>>> 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()

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

>>> 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)

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

>>> 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)

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

>>> 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)

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

>>> 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_args_list проверяется на наличие ожиданий.

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

Если any_order истинно, то ожидания могут быть в любом порядке, но они должны все присутствовать в 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()

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

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

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

await_count

Целое число, отслеживающее, сколько раз объект заглушки был ожидаем.

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

Это либо None (если заглушка не была ожидана), либо аргументы, с которыми заглушка была в последнюю очередь ожидана. Функционально аналогично 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

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

>>> 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 вызывает исключение, вызов всё равно записывается.

Самый простой способ сделать так, чтобы вызов Mock вызывал исключение, заключается в том, чтобы сделать 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 является функцией, то то, что эта функция возвращает, и будет возвращать вызов Mock. Функция side_effect вызывается с теми же аргументами, что и Mock. Это позволяет динамически изменять возвращаемое значение вызова в зависимости от входных данных:

>>> 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 всё равно возвращал значение по умолчанию (новый mock) или любое установленное возвращаемое значение, есть два способа. Либо верните 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 также может быть любым итерируемым объектом. Повторные вызовы Mock будут возвращать значения из итерируемого объекта (пока итерируемый объект не исчерпан и не возникает 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 для mock, но это не всегда удобно.

Вы «блокируете» атрибуты, удаляя их. После удаления обращение к атрибуту вызовет 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

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

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

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

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

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

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

>>> 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 имеет имя. Это позволяет предотвратить «родительские» отношения, если по какой-либо причине этого не нужно.

>>> 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
[]

Созданные для вас mock с помощью patch() автоматически получают имена. Чтобы прикрепить mock с именами к родителю, используйте метод 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, заменяющих класс, их возвращаемое значение («экземпляр») будет иметь ту же спецификацию, что и класс. См. функцию 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 равно true, то словарь будет очищен перед установкой новых значений.

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
...

методы патчей: 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' как тестовые методы. Это тот же способ, которым 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 применяет декораторы. Порядок созданных моков, переданных в вашу тестовую функцию, соответствует этому порядку.

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

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')

Однако, рассмотрим альтернативный сценарий, где вместо 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__, __truediv__, __floordiv__, __mod__, __divmod__, __lshift__, __rshift__, __and__, __xor__, __or__, и __pow__
  • Методы числового преобразования: __complex__, __int__, __float__ и __index__
  • Методы дескрипторов: __get__, __set__ и __delete__
  • Пиклирование: __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__

Magic Mock

Существует два 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__
[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, то попытка установить атрибуты, которых нет в объекте spec, вызовет 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

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

class TestStringMethods(unittest.TestCase):

    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', ANY])

FILTER_DIR

unittest.mock.FILTER_DIR

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

При включенной фильтрации dir(some_mock) отображает только полезные атрибуты и включает все динамически созданные атрибуты, которые обычно не отображаются. Если подделка создавалась со спецификацией (или, конечно, 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 перематывается в начало. Если вам нужен больший контроль над данными, которые вы передаёте тестируемому коду, вам потребуется настроить этот мок самостоятельно. Если этого недостаточно, пакеты системных файлов в памяти на PyPI могут предложить реалистичную файловую систему для тестирования.

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

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

Изменено в версии 3.8: В реализацию добавлена __iter__(), поэтому итерация (например, в циклах 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, но использует подделки вместо реальных объектов, все равно пройдут. Это означает, что ваши тесты могут пройти успешно, даже если ваш код сломан.

Изменено в версии 3.5: До версии 3.5 тесты с ошибкой в слове assert бесшумно проходили, когда должны были генерировать ошибку. Вы по-прежнему можете добиться такого поведения, передав unsafe=True в Mock.

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

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='...'>

Однако это не лишено нюансов и ограничений, поэтому оно не является стандартным поведением. Чтобы узнать, какие атрибуты доступны в объекте спецификации, autospec должен инспектировать (получать доступ к атрибутам) спецификацию. При прохождении по атрибутам подделки одновременно происходит соответствующее прохождение по исходному объекту. Если какие-либо ваши объекты со спецификацией имеют свойства или дескрипторы, которые могут вызывать выполнение кода, вы можете не иметь возможности использовать autospec. С другой стороны, лучше разработать свои объекты так, чтобы интроспекция была безопасной [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. Они будут обычными подделками (ну — MagicMock):

>>> 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)

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

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

>>> 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.

Порядок приоритета side_effect, return_value и wraps

Порядок их приоритета:

  1. side_effect
  2. return_value
  3. wraps

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

>>> from unittest.mock import Mock
>>> class Order:
...     @staticmethod
...     def get_value():
...         return "third"
...
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first'

Так как None является значением по умолчанию для side_effect, если вы переназначите его обратно на None, порядок приоритета будет проверен между return_value и обернутым объектом, проигнорировав side_effect.

>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'

Если возвращаемое значение side_effect равно DEFAULT, оно игнорируется, и порядок приоритета переходит к преемнику для получения возвращаемого значения.

>>> from unittest.mock import DEFAULT
>>> order_mock.get_value.side_effect = [DEFAULT]
>>> order_mock.get_value()
'second'

Когда Mock оборачивает объект, значение по умолчанию для return_value будет DEFAULT.

>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.return_value
sentinel.DEFAULT
>>> order_mock.get_value.return_value
sentinel.DEFAULT

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

Поскольку реальный вызов происходит к обернутому объекту, создание экземпляра этого mock вернёт реальный экземпляр класса. Позиционные аргументы, если они есть, необходимые обернутому объекту, должны быть переданы.

>>> order_mock_instance = order_mock()
>>> isinstance(order_mock_instance, Order)
True
>>> order_mock_instance.get_value()
'third'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'second'

Но если вы назначите None ему, это не будет проигнорировано, так как это явное назначение. Таким образом, порядок приоритета не перейдёт к обернутому объекту.

>>> order_mock.get_value.return_value = None
>>> order_mock.get_value() is None
True

Даже если вы зададите все три значения одновременно при инициализации mock, порядок приоритета остаётся тем же:

>>> order_mock = Mock(spec=Order, wraps=Order,
...                   **{"get_value.side_effect": ["first"],
...                      "get_value.return_value": "second"}
...                   )
...
>>> order_mock.get_value()
'first'
>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'

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

>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first side effect value",
...                                     "another side effect value"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first side effect value'
>>> order_mock.get_value()
'another side effect value'
>>> order_mock.get_value()
Traceback (most recent call last):
 ...
StopIteration

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

Spec-Zone.ru

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