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: missing a required argument: 'b'
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: Значение, возвращаемое при вызове подделки. По умолчанию это новая подделка (созданная при первом доступе). См. атрибут
return_value. -
unsafe: По умолчанию доступ к любому атрибуту, имя которого начинается с assert, assret, asert, aseert или assrt, вызовет
AttributeError. Передачаunsafe=Trueпозволит получить доступ к этим атрибутам.Добавлена в версии 3.5.
-
wraps: Элемент для обертывания объекта подделки. Если 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. Calls: [call(), call()].
Добавлена в версии 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. Calls: [call('foo', bar='baz'), call('other', bar='values')].
-
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 равно false, вызовы должны быть последовательными. До или после указанных вызовов могут быть дополнительные вызовы.
Если any_order равно true, вызовы могут быть в любом порядке, но они все должны появиться в
mock_calls.>>> mock = Mock(return_value=None) >>> mock(1) >>> mock(2) >>> mock(3) >>> mock(4) >>> calls = [call(2), call(3)] >>> mock.assert_has_calls(calls) >>> calls = [call(4), call(2), call(3)] >>> mock.assert_has_calls(calls, any_order=True)
-
assert_not_called() -
Утверждение, что подделка никогда не вызывалась.
>>> 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. Calls: [call()].
Добавлена в версии 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 равно true, то только атрибуты в спецификации могут быть установлены.
-
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): ... KeyErrorconfigure_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
Вызываемый мок, который был создан со спецификацией (или набором спецификаций), будет инспектировать сигнатуру объекта спецификации при сопоставлении вызовов моку. Поэтому он может сопоставлять фактические аргументы вызова независимо от того, передавались ли они позиционно или по имени:
>>> 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.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 await 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')]
- Если
-
class unittest.mock.ThreadingMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, *, timeout=UNSET, **kwargs) -
Версия
MagicMockдля тестов в многопоточных приложениях. ОбъектThreadingMockпредоставляет дополнительные методы для ожидания вызова, вместо немедленного утверждения о нём.Значение таймаута по умолчанию задаётся параметром
timeout, или, если не задано, атрибутомThreadingMock.DEFAULT_TIMEOUT, который по умолчанию блокирующий (None).Вы можете настроить глобальный таймаут по умолчанию, установив
ThreadingMock.DEFAULT_TIMEOUT.-
wait_until_called(*, timeout=UNSET) -
Ожидает вызова мока.
Если при создании мока был указан таймаут или аргумент таймаута передан в эту функцию, функция вызывает
AssertionError, если вызов не выполнен в срок.>>> mock = ThreadingMock() >>> thread = threading.Thread(target=mock) >>> thread.start() >>> mock.wait_until_called(timeout=1) >>> thread.join()
-
wait_until_any_call_with(*args, **kwargs) -
Ожидает вызова мока с указанными аргументами.
Если при создании мока был указан таймаут, функция вызывает
AssertionError, если вызов не выполнен в срок.>>> mock = ThreadingMock() >>> thread = threading.Thread(target=mock, args=("arg1", "arg2",), kwargs={"arg": "thing"}) >>> thread.start() >>> mock.wait_until_any_call_with("arg1", "arg2", arg="thing") >>> thread.join()
-
DEFAULT_TIMEOUT -
Глобальный таймаут по умолчанию в секундах для создания экземпляров
ThreadingMock.
Добавлен в версии 3.13.
-
Вызов
Объекты Mock вызываемы. Вызов вернёт значение, установленное в качестве атрибута return_value. Значение по умолчанию — новый объект Mock; он создаётся в первый раз, когда значение возврата используется (явно или при вызове Mock) — но он хранится и возвращается каждый раз.
Вызовы объекта будут записаны в атрибуты, такие как call_args и call_args_list.
Если установлен side_effect, он будет вызван после того, как вызов будет записан, поэтому, если side_effect вызывает исключение, вызов всё равно записывается.
Самый простой способ заставить мок вызывать исключение при вызове — сделать side_effect классом исключения или экземпляром:
>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]
Если side_effect является функцией, то то, что возвращает эта функция, и есть то, что возвращают вызовы мока. Функция side_effect вызывается с теми же аргументами, что и мок. Это позволяет динамически изменять значение возврата вызова в зависимости от входных данных:
>>> def side_effect(value): ... return value + 1 ... >>> m = MagicMock(side_effect=side_effect) >>> m(1) 2 >>> m(2) 3 >>> m.mock_calls [call(1), call(2)]
Если вы хотите, чтобы мок всё ещё возвращал значение по умолчанию (новый мок) или любое заданное значение возврата, есть два способа. Либо вернуть 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 создают атрибуты по запросу. Это позволяет им имитировать объекты любого типа.
Вам может понадобиться, чтобы объект мока возвращал False в вызове hasattr() или поднимал AttributeError, когда запрашивается атрибут. Вы можете сделать это, предоставив объект в качестве spec для мока, но это не всегда удобно.
Вы «блокируете» атрибуты, удаляя их. После удаления обращение к атрибуту вызовет AttributeError.
>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
...
AttributeError: f
Имена моков и атрибут name
Поскольку «name» является аргументом конструктора Mock, если вы хотите, чтобы ваш объект мока имел атрибут «name», вы не можете просто передать его при создании. Есть два варианта. Один вариант — использовать configure_mock():
>>> mock = MagicMock() >>> mock.configure_mock(name='my_name') >>> mock.name 'my_name'
Более простой вариант — просто установить атрибут «name» после создания мока:
>>> mock = MagicMock() >>> mock.name = "foo"
Прикрепление моков в качестве атрибутов
Когда вы прикрепляете мок в качестве атрибута другого мока (или в качестве значения возврата), он становится «ребёнком» этого мока. Вызовы ребёнка записываются в атрибутах method_calls и mock_calls родителя. Это полезно для настройки дочерних моков и их прикрепления к родителю, или для прикрепления моков к родителю, который записывает все вызовы детей и позволяет делать утверждения о порядке вызовов между моками:
>>> parent = MagicMock() >>> child1 = MagicMock(return_value=None) >>> child2 = MagicMock(return_value=None) >>> parent.child1 = child1 >>> parent.child2 = child2 >>> child1(1) >>> child2(2) >>> parent.mock_calls [call.child1(1), call.child2(2)]
Исключением является случай, когда у мока есть имя. Это позволяет предотвратить «родительство», если по какой-то причине этого не требуется.
>>> mock = MagicMock() >>> not_a_child = MagicMock(name='not-a-child') >>> mock.attribute = not_a_child >>> mock.attribute() <MagicMock name='not-a-child()' id='...'> >>> mock.mock_calls []
Моки, созданные для вас patch(), автоматически получают имена. Чтобы прикрепить моки с именами к родителю, используйте метод attach_mock():
>>> thing1 = object()
>>> thing2 = object()
>>> parent = MagicMock()
>>> with patch('__main__.thing1', return_value=None) as child1:
... with patch('__main__.thing2', return_value=None) as child2:
... parent.attach_mock(child1, 'child1')
... parent.attach_mock(child2, 'child2')
... child1('one')
... child2('two')
...
>>> parent.mock_calls
[call.child1('one'), call.child2('two')]
Модули патчинга
Декораторы патчинга используются для патчинга объектов только в пределах области функции, которую они декорируют. Они автоматически обрабатывают отмену патчинга, даже если возникают исключения. Все эти функции также могут быть использованы в операторах 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, если они вызываются с неправильной сигнатурой. Для моков, заменяющих класс, их возвращаемое значение («экземпляр») будет иметь ту же спецификацию, что и класс. См. функцию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()
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
...
методы patch: start и stop
Все патчеры имеют методы start() и stop(). Они упрощают работу с заменами в методах setUp или там, где вам нужно выполнить несколько замен без вложенных декораторов или инструкций with.
Для их использования вызовите patch(), patch.object() или patch.dict() как обычно и сохраните ссылку на возвращённый patcher объект. Затем вы можете вызвать start() для размещения замены и stop() для её отмены.
Если вы используете patch() для создания заглушки, то она будет возвращена по вызову patcher.start.
>>> patcher = patch('package.module.ClassName')
>>> from package import module
>>> original = module.ClassName
>>> new_mock = patcher.start()
>>> assert module.ClassName is not original
>>> assert module.ClassName is new_mock
>>> patcher.stop()
>>> assert module.ClassName is original
>>> assert module.ClassName is not new_mock
Типичный пример использования — выполнение нескольких замен в методе setUp класса TestCase:
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... self.patcher1 = patch('package.module.Class1')
... self.patcher2 = patch('package.module.Class2')
... self.MockClass1 = self.patcher1.start()
... self.MockClass2 = self.patcher2.start()
...
... def tearDown(self):
... self.patcher1.stop()
... self.patcher2.stop()
...
... def test_something(self):
... assert package.module.Class1 is self.MockClass1
... assert package.module.Class2 is self.MockClass2
...
>>> MyTest('test_something').run()
Предупреждение
Если вы используете этот метод, убедитесь, что замена «отменена» вызовом stop. Это может оказаться сложнее, чем вы думаете, так как если в методе setUp произойдёт исключение, то tearDown не будет вызван. unittest.TestCase.addCleanup() упрощает эту задачу:
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... patcher = patch('package.module.Class')
... self.MockClass = patcher.start()
... self.addCleanup(patcher.stop)
...
... def test_something(self):
... assert package.module.Class is self.MockClass
...
В качестве дополнительного бонуса вам больше не нужно хранить ссылку на patcher объект.
Также можно остановить все начатые замены с помощью patch.stopall().
-
patch.stopall() -
Остановить все активные замены. Останавливает только те замены, которые были начаты с помощью
start.
патч встроенных функций
Вы можете изменить любые встроенные функции в модуле. Следующий пример патчит встроенную функцию ord():
>>> @patch('__main__.ord')
... def test(mock_ord):
... mock_ord.return_value = 101
... print(ord('c'))
...
>>> test()
101
TEST_PREFIX
Все патчеры могут использоваться как декораторы классов. При таком использовании они обертывают каждый тестовый метод в классе. Патчеры распознают методы, начинающиеся с 'test' как тестовые методы. Это тот же способ, которым unittest.TestLoader находит тестовые методы по умолчанию.
Возможно, вы захотите использовать другой префикс для своих тестов. Вы можете сообщить патчерам об этом префиксе, установив patch.TEST_PREFIX:
>>> patch.TEST_PREFIX = 'foo'
>>> value = 3
>>>
>>> @patch('__main__.value', 'not three')
... class Thing:
... def foo_one(self):
... print(value)
... def foo_two(self):
... print(value)
...
>>>
>>> Thing().foo_one()
not three
>>> Thing().foo_two()
not three
>>> value
3
Вложенные декораторы патча
Если вы хотите выполнить несколько патчей, то вы можете просто наложить декораторы друг на друга.
Вы можете наложить несколько декораторов патчей, используя этот шаблон:
>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
... assert SomeClass.static_method is mock1
... assert SomeClass.class_method is mock2
... SomeClass.static_method('foo')
... SomeClass.class_method('bar')
... return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')
Обратите внимание, что декораторы применяются сверху вниз. Это стандартный способ применения декораторов в Python. Порядок созданных моков, передаваемых в вашу тестовую функцию, соответствует этому порядку.
Где производить патч
patch() работает, временно изменяя объект, на который указывает имя, на другой. Может быть много имён, указывающих на любой отдельный объект, поэтому для работы патча необходимо убедиться, что вы патчите имя, используемое тестируемой системой.
Основной принцип заключается в том, что вы патчите то место, где ищется объект, а это не обязательно то же самое место, где он определяется. Несколько примеров помогут прояснить это.
Представьте, что у нас есть проект, который мы хотим протестировать со следующей структурой:
a.py
-> Defines SomeClass
b.py
-> from a import SomeClass
-> some_function instantiates SomeClass
Теперь мы хотим протестировать some_function, но хотим смоделировать SomeClass с помощью patch(). Проблема в том, что когда мы импортируем модуль b, что нам придётся сделать, он импортирует SomeClass из модуля a. Если мы используем patch() для моделирования a.SomeClass, то это не повлияет на наш тест; модуль b уже имеет ссылку на реальный SomeClass и кажется, что наш патч не повлиял.
Ключ — в том, чтобы заменить SomeClass там, где он используется (или где он ищется). В этом случае some_function будет на самом деле искать SomeClass в модуле b, куда мы его импортировали. Патч должен выглядеть так:
@patch('b.SomeClass')
Однако, рассмотрите альтернативный сценарий, где вместо from a import
SomeClass модуль b делает import a и some_function использует a.SomeClass. Оба эти способа импорта распространены. В этом случае класс, который мы хотим патчить, ищется в модуле, и поэтому нам нужно патчить a.SomeClass вместо этого:
@patch('a.SomeClass')
Патчинг дескрипторов и прокси-объектов
И patch, и patch.object корректно патчат и восстанавливают дескрипторы: методы класса, статические методы и свойства. Вы должны патчить их в классе, а не в экземпляре. Они также работают с некоторыми объектами, которые проксируют доступ к атрибутам, например, с объектом настроек django.
Поддержка MagicMock и магических методов
Моделирование магических методов
Mock поддерживает моделирование методов протокола Python, также известных как “магические методы”. Это позволяет объектам-мокам заменять контейнеры или другие объекты, реализующие протоколы Python.
Поскольку магические методы ищутся по-другому, чем обычные методы [2], эта поддержка была реализована специально. Это означает, что поддерживаются только определенные магические методы. Поддерживаемый список включает почти все из них. Если вы заметите какие-либо отсутствующие, сообщите нам, пожалуйста.
Вы моделируете магические методы, устанавливая интересующий метод в функцию или экземпляр мока. Если вы используете функцию, она должна принимать self в качестве первого аргумента [3].
>>> def __str__(self): ... return 'fooble' ... >>> mock = Mock() >>> mock.__str__ = __str__ >>> str(mock) 'fooble'
>>> mock = Mock() >>> mock.__str__ = Mock() >>> mock.__str__.return_value = 'fooble' >>> str(mock) 'fooble'
>>> mock = Mock() >>> mock.__iter__ = Mock(return_value=iter([])) >>> list(mock) []
Один из вариантов использования этого — моделирование объектов, используемых в качестве менеджеров контекста в операторе with:
>>> mock = Mock() >>> mock.__enter__ = Mock(return_value='foo') >>> mock.__exit__ = Mock(return_value=False) >>> with mock as m: ... assert m == 'foo' ... >>> mock.__enter__.assert_called_with() >>> mock.__exit__.assert_called_with(None, None, None)
Вызовы магических методов не отображаются в method_calls, но они записываются в mock_calls.
Примечание
Если вы используете ключевой аргумент spec для создания мока, попытка установить магический метод, который не указан в спецификации, вызовет AttributeError.
Полный список поддерживаемых магических методов:
-
__hash__,__sizeof__,__repr__и__str__ -
__dir__,__format__и__subclasses__ -
__round__,__floor__,__trunc__и__ceil__ - Сравнения:
__lt__,__gt__,__le__,__ge__,__eq__и__ne__ - Методы контейнеров:
__getitem__,__setitem__,__delitem__,__contains__,__len__,__iter__,__reversed__и__missing__ - Менеджер контекста:
__enter__,__exit__,__aenter__и__aexit__ - Унарные числовые методы:
__neg__,__pos__и__invert__ - Числовые методы (включая варианты с правой частью и операциями на месте):
__add__,__sub__,__mul__,__matmul__,__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__.
Следующие методы существуют, но не поддерживаются, поскольку они либо используются моком, либо не могут быть динамически установлены, или могут вызывать проблемы:
-
__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__
Магические методы должны искаться в классе, а не в экземпляре. Разные версии Python несогласованно применяют это правило. Поддерживаемые методы протокола должны работать со всеми поддерживаемыми версиями Python.
Функция в основном подключается к классу, но каждый экземпляр Mock остается изолированным.
Справочные методы
sentinel
-
unittest.mock.sentinel -
Объект
sentinelпредоставляет удобный способ предоставления уникальных объектов для ваших тестов.Атрибуты создаются по мере необходимости при обращении к ним по имени. Обращение к одному и тому же атрибуту всегда возвращает один и тот же объект. Возвращаемые объекты имеют осмысленное представление, чтобы сообщения об ошибках тестов были удобочитаемыми.
Иногда при тестировании необходимо проверить, что конкретный объект передается в качестве аргумента другому методу или возвращается. Для тестирования этого часто создаются именованные объекты-маяки. 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_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
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) отображает только полезные атрибуты и включает любые динамически созданные атрибуты, которые обычно не отображаются. Если мок был создан с помощью spec (или autospec, конечно), то показываются все атрибуты из оригинала, даже если они ещё не были обработаны:
>>> dir(Mock()) ['assert_any_call', 'assert_called', 'assert_called_once', 'assert_called_once_with', 'assert_called_with', 'assert_has_calls', 'assert_not_called', 'attach_mock', ... >>> from urllib import request >>> dir(Mock(spec=request)) ['AbstractBasicAuthHandler', 'AbstractDigestAuthHandler', 'AbstractHTTPHandler', 'BaseHandler', ...
Многие бесполезные (принадлежащие Mock, а не объекту, который имитируется) атрибуты, начинающиеся с подчеркивания и двойного подчеркивания, были отфильтрованы из результата вызова dir() для Mock. Если вам не нравится это поведение, вы можете отключить его, установив переключатель уровня модуля FILTER_DIR:
>>> from unittest import mock >>> mock.FILTER_DIR = False >>> dir(mock.Mock()) ['_NonCallableMock__get_return_value', '_NonCallableMock__get_side_effect', '_NonCallableMock__return_value_doc', '_NonCallableMock__set_return_value', '_NonCallableMock__set_side_effect', '__call__', '__class__', ...
В качестве альтернативы, вы можете использовать vars(my_mock) (члены экземпляра) и dir(type(my_mock)) (члены типа), чтобы обойти фильтрацию независимо от значения 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.
Обратите внимание, что это еще одна причина, по которой вам нужны интеграционные тесты наряду с модульными. Тестирование всего в изоляции — это хорошо и замечательно, но если вы не тестируете, как ваши модули «связаны» друг с другом, остаётся много места для ошибок, которые тесты могли бы обнаружить.
unittest.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 должен провести интроспекцию (получить доступ к атрибутам) спецификации. При переходе по атрибутам мока происходит соответствующий переход по исходному объекту. Если у ваших объектов со спецификацией есть свойства или описатели, которые могут вызвать выполнение кода, вы, возможно, не сможете использовать автоспецификацию. С другой стороны, гораздо лучше спроектировать ваши объекты так, чтобы интроспекция была безопасной [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='...'>
Это относится только к классам или уже созданным объектам. Вызов смоделированного класса для создания экземпляра мока не создаёт реальный экземпляр. Это только просмотр атрибутов — наряду с вызовами к 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 и обёртки
Порядок их приоритета:
side_effectreturn_value- обёртки
Если все три установлены, 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.13/library/unittest.mock.html