Spec-Zone.ru › Python 3.8

unittest.mock — библиотека объектов-заглушек

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Асинхронная версия Mock. Объект 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 равно false, ожидания должны быть последовательными. До или после указанных ожиданий могут быть дополнительные вызовы.

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

Вызов

Объекты-заглушки вызываемы. Вызов вернёт значение, установленное как атрибут 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) или любое заданное возвращаемое значение, есть два способа. Либо вернуть 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 для 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

Примечание

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

но добавление 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)

Заменить указанный член (атрибут) объекта (целевой объект) на объект-заглушку.

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.

Модуль встроенных функций

Вы можете подменить любые встроенные функции в модуле. Следующий пример заменяет встроенную функцию 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')

Однако, рассмотрим альтернативный сценарий, в котором вместо 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 для создания модели, попытка установить магический метод, который не указан в 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__
  • Сериализация: __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 предоставляет удобный способ создания и проверки идентичности таких объектов.

В этом примере мы используем monkey-patch для замены 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
>>> sentinel.some_object
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) отображаются только полезные атрибуты, а также любые динамически созданные атрибуты, которые обычно не отображаются. Если модель была создана со спецификацией (или, конечно, 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 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)

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

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

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

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

>>> from urllib import request
>>> mock = Mock(spec=request.Request)
>>> mock.assret_called_with
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()

Автоспецификация решает эту проблему. Вы можете либо передать 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
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. С другой стороны, гораздо лучше разработать свои объекты так, чтобы интроспекция была безопасной 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–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/library/unittest.mock.html

Spec-Zone.ru

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