unittest.mock — библиотека объектов-заглушек
Добавлена в версии 3.3.
Исходный код: Lib/unittest/mock.py
unittest.mock — это библиотека для тестирования на Python. Она позволяет заменить части вашей тестируемой системы объектами-заглушками и делать утверждения о том, как они были использованы.
unittest.mock предоставляет базовый класс Mock, устраняя необходимость создавать множество заглушек в вашем наборе тестов. После выполнения действия вы можете сделать утверждения о том, какие методы/атрибуты были использованы и с какими аргументами они были вызваны. Вы также можете указать возвращаемые значения и установить необходимые атрибуты обычным способом.
Кроме того, mock предоставляет декоратор patch(), который обрабатывает подмену атрибутов модуля и класса в рамках теста, а также sentinel для создания уникальных объектов. См. краткое руководство, чтобы узнать, как использовать Mock, MagicMock и patch().
Mock предназначен для использования с unittest и основан на шаблоне «действие -> утверждение» вместо «запись -> воспроизведение», используемом во многих фреймворках для создания заглушек.
Существует портабельная версия unittest.mock для более ранних версий Python, доступная как mock на PyPI.
Краткое руководство
Mock и MagicMock объекты создают все атрибуты и методы по мере их доступа и сохраняют подробности о том, как они были использованы. Вы можете настроить их, указав значения возврата или ограничив доступные атрибуты, а затем сделать утверждения о том, как они были использованы:
>>> from unittest.mock import MagicMock >>> thing = ProductionClass() >>> thing.method = MagicMock(return_value=3) >>> thing.method(3, 4, 5, key='value') 3 >>> thing.method.assert_called_with(3, 4, 5, key='value')
side_effect позволяет выполнять побочные эффекты, включая выброс исключения при вызове объекта-заглушки:
>>> from unittest.mock import Mock
>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()
Traceback (most recent call last):
...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect(arg):
... return values[arg]
...
>>> mock.side_effect = side_effect
>>> mock('a'), mock('b'), mock('c')
(1, 2, 3)
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)
У Mock есть много других способов настройки и управления его поведением. Например, аргумент spec настраивает заглушку на получение спецификаций из другого объекта. Попытка доступа к атрибутам или методам заглушки, которые не существуют в спецификации, приведет к ошибке AttributeError.
Декоратор/менеджер контекста patch() упрощает подмену классов или объектов в тестируемом модуле. Указанный объект будет заменен заглушкой (или другим объектом) во время теста и восстановлен по завершении теста:
>>> from unittest.mock import patch
>>> @patch('module.ClassName2')
... @patch('module.ClassName1')
... def test(MockClass1, MockClass2):
... module.ClassName1()
... module.ClassName2()
... assert MockClass1 is module.ClassName1
... assert MockClass2 is module.ClassName2
... assert MockClass1.called
... assert MockClass2.called
...
>>> test()
Примечание
При вложенном использовании декораторов patch заглушки передаются в декорированную функцию в том же порядке, в котором они были применены (обычный порядок Python для декораторов). Это означает сверху вниз, поэтому в примере выше заглушка для module.ClassName1 передается первой.
С patch() важно, чтобы вы подменяли объекты в том пространстве имен, где они ищут их. Как правило, это просто, но для быстрого руководства прочтите где подменять.
Помимо декоратора, patch() может использоваться как менеджер контекста в операторе with:
>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method: ... thing = ProductionClass() ... thing.method(1, 2, 3) ... >>> mock_method.assert_called_once_with(1, 2, 3)
Также есть patch.dict() для установки значений в словаре только в рамках области видимости и восстановления словаря до его исходного состояния по окончании теста:
>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
... assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original
Mock поддерживает подмену магических методов Python магические методы. Самый простой способ использования магических методов — с классом MagicMock. Он позволяет делать такие вещи, как:
>>> mock = MagicMock() >>> mock.__str__.return_value = 'foobarbaz' >>> str(mock) 'foobarbaz' >>> mock.__str__.assert_called_with()
Mock позволяет назначать функции (или другие экземпляры Mock) магическим методам, и они будут вызываться соответствующим образом. Класс MagicMock — это просто вариант Mock, в котором все магические методы созданы заранее (по крайней мере, все полезные).
Ниже приведен пример использования магических методов с обычным классом Mock:
>>> mock = Mock() >>> mock.__str__ = Mock(return_value='wheeeeee') >>> str(mock) 'wheeeeee'
Для обеспечения того, чтобы объекты-заглушки в ваших тестах имели тот же API, что и заменяемые объекты, вы можете использовать авто-спецификацию. Авто-спецификацию можно выполнить с помощью аргумента autospec для patch или функции create_autospec(). Авто-спецификация создает объекты-заглушки, которые имеют те же атрибуты и методы, что и заменяемые объекты, и любые функции и методы (включая конструкторы) имеют такую же сигнатуру вызова, как и реальный объект.
Это гарантирует, что ваши заглушки будут выдавать такие же ошибки, как и ваш рабочий код, если они будут использованы неправильно:
>>> from unittest.mock import create_autospec
>>> def function(a, b, c):
... pass
...
>>> mock_function = create_autospec(function, return_value='fishy')
>>> mock_function(1, 2, 3)
'fishy'
>>> mock_function.assert_called_once_with(1, 2, 3)
>>> mock_function('wrong arguments')
Traceback (most recent call last):
...
TypeError: <lambda>() takes exactly 3 arguments (1 given)
create_autospec() также может использоваться для классов, где он копирует сигнатуру метода __init__, и для вызываемых объектов, где он копирует сигнатуру метода __call__.
Класс Mock
Mock — гибкий объект-заглушка, предназначенный для замены использования заглушек и тестовых удвоений в вашем коде. Объекты-заглушки вызываемы и создают атрибуты как новые заглушки при их доступе [1]. Доступ к одному и тому же атрибуту всегда возвращает одну и ту же заглушку. Заглушки записывают, как вы их используете, что позволяет вам делать утверждения о том, что ваш код с ними делал.
MagicMock — подкласс Mock со всеми магическими методами, предварительно созданными и готовыми к использованию. Также есть варианты, не являющиеся вызываемыми, полезные при заглушении объектов, которые не вызываемы: NonCallableMock и NonCallableMagicMock
Декораторы patch() упрощают временную замену классов в определенном модуле объектом Mock. По умолчанию patch() создаст MagicMock для вас. Вы можете указать альтернативный класс Mock с помощью аргумента new_callable для patch().
-
class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)
-
Создайте новый объект
Mock.Mockпринимает несколько необязательных аргументов, которые задают поведение объекта Mock:-
spec: Это может быть либо список строк, либо существующий объект (класс или экземпляр), который служит спецификацией для объекта Mock. Если вы передаёте объект, то список строк формируется путём вызова dir на объекте (исключая неподдерживаемые магические атрибуты и методы). Доступ к любому атрибуту, отсутствующему в этом списке, вызовет
AttributeError.Если spec является объектом (а не списком строк), то
__class__возвращает класс объекта spec. Это позволяет мокам проходить проверкиisinstance(). -
spec_set: Более строгая разновидность spec. Если используется, попытка установить или получить атрибут в моке, который не присутствует в объекте, переданном как spec_set, вызовет
AttributeError. -
side_effect: Функция, вызываемая всякий раз, когда вызывается Mock. См. атрибут
side_effect. Полезно для поднятия исключений или динамического изменения возвращаемых значений. Функция вызывается с теми же аргументами, что и Mock, и, если она не возвращаетDEFAULT, возвращаемое значение этой функции используется как возвращаемое значение.В качестве альтернативы, side_effect может быть классом или экземпляром исключения. В этом случае исключение будет поднято при вызове мока.
Если side_effect является итерируемым объектом, каждый вызов мока вернёт следующее значение из итерируемого объекта.
side_effect может быть очищен, установив его в
None. -
return_value: Значение, возвращаемое при вызове мока. По умолчанию это новый Mock (созданный при первом доступе). См. атрибут
return_value. -
unsafe: По умолчанию доступ к любому атрибуту, имя которого начинается с assert, assret, asert, aseert или assrt, вызовет
AttributeError. Передачаunsafe=Trueпозволит получить доступ к этим атрибутам.Добавлена в версии 3.5.
-
wraps: Элемент для обертывания объекта Mock. Если wraps не
None, то вызов Mock передаст вызов обернутому объекту (возвращая реальный результат). Доступ к атрибуту мока вернёт объект Mock, который оборачивает соответствующий атрибут обернутого объекта (попытка доступа к атрибуту, которого не существует, вызоветAttributeError).Если у мока явно задано значение return_value, вызовы не передаются обернутому объекту, а возвращается return_value вместо этого.
- name: Если у мока есть имя, оно будет использовано в repr мока. Это может быть полезно для отладки. Имя передаётся дочерним мокам.
Моки также могут вызываться с произвольными именованными аргументами. Эти аргументы будут использоваться для установки атрибутов в моке после его создания. Подробности см. в методе
configure_mock().-
assert_called() -
Проверить, что мок был вызван хотя бы один раз.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called()
Добавлена в версии 3.6.
-
assert_called_once() -
Проверить, что мок был вызван ровно один раз.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() Traceback (most recent call last): ... AssertionError: Expected 'method' to have been called once. Called 2 times.
Добавлена в версии 3.6.
-
assert_called_with(*args, **kwargs) -
Этот метод является удобным способом проверки того, что последний вызов был сделан определённым образом:
>>> mock = Mock() >>> mock.method(1, 2, 3, test='wow') <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_with(1, 2, 3, test='wow')
-
assert_called_once_with(*args, **kwargs) -
Проверить, что мок был вызван ровно один раз и что этот вызов был с указанными аргументами.
>>> mock = Mock(return_value=None) >>> mock('foo', bar='baz') >>> mock.assert_called_once_with('foo', bar='baz') >>> mock('other', bar='values') >>> mock.assert_called_once_with('other', bar='values') Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times.
-
assert_any_call(*args, **kwargs) -
Проверить, что мок был вызван с указанными аргументами.
Проверка пройдёт, если мок был вызван хоть один раз, в отличие от
assert_called_with()иassert_called_once_with(), которые проходят только если вызов является последним, и в случаеassert_called_once_with()он также должен быть единственным вызовом.>>> mock = Mock(return_value=None) >>> mock(1, 2, arg='thing') >>> mock('some', 'thing', 'else') >>> mock.assert_any_call(1, 2, arg='thing')
-
assert_has_calls(calls, any_order=False) -
Проверить, что мок был вызван с указанными вызовами. Проверяется список
mock_calls.Если any_order ложно, то вызовы должны быть последовательными. До или после указанных вызовов могут быть дополнительные вызовы.
Если any_order истинно, то вызовы могут быть в любом порядке, но все они должны присутствовать в
mock_calls.>>> mock = Mock(return_value=None) >>> mock(1) >>> mock(2) >>> mock(3) >>> mock(4) >>> calls = [call(2), call(3)] >>> mock.assert_has_calls(calls) >>> calls = [call(4), call(2), call(3)] >>> mock.assert_has_calls(calls, any_order=True)
-
assert_not_called() -
Проверить, что мок не был вызван ни разу.
>>> m = Mock() >>> m.hello.assert_not_called() >>> obj = m.hello() >>> m.hello.assert_not_called() Traceback (most recent call last): ... AssertionError: Expected 'hello' to not have been called. Called 1 times.
Добавлена в версии 3.5.
-
reset_mock(*, return_value=False, side_effect=False) -
Метод reset_mock сбрасывает все атрибуты вызовов в объекте мока:
>>> mock = Mock(return_value=None) >>> mock('hello') >>> mock.called True >>> mock.reset_mock() >>> mock.called FalseИзменено в версии 3.6: Добавлены два именованных аргумента в функцию reset_mock.
Это может быть полезно, когда вы хотите выполнить серию проверок, повторно используя один и тот же объект. Обратите внимание, что
reset_mock()не очищаетreturn_value,side_effectили любые атрибуты дочерних объектов, которые вы задали с помощью обычного присваивания по умолчанию. Если вы хотите сброситьreturn_valueилиside_effect, передайте соответствующий параметр какTrue. Дочерние моки и мок возвращаемого значения (если таковой имеется) также сбрасываются.Примечание
return_value и side_effect являются именованными аргументами.
-
mock_add_spec(spec, spec_set=False) -
Добавить спецификацию в мок. spec может быть объектом или списком строк. Только атрибуты в spec могут быть получены как атрибуты из мока.
Если spec_set истинно, то только атрибуты в спецификации могут быть установлены.
-
attach_mock(mock, attribute) -
Прикрепить мок в качестве атрибута к этому, заменив его имя и родителя. Вызовы прикреплённого мока будут записаны в атрибуты
method_callsиmock_callsэтого.
-
configure_mock(**kwargs) -
Установить атрибуты в моке через именованные аргументы.
Атрибуты плюс возвращаемые значения и побочные эффекты могут быть установлены в дочерних моках с использованием стандартной нотации точки и распаковки словаря в вызове метода:
>>> mock = Mock() >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock.configure_mock(**attrs) >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyErrorТо же самое можно сделать в вызове конструктора для моков:
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock = Mock(some_attribute='eggs', **attrs) >>> mock.some_attribute 'eggs' >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... 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Вызываемая модель, которая была создана со спецификацией (или spec_set), будет выполнять интроспекцию подписи объекта спецификации при сопоставлении вызовов модели. Таким образом, она может сопоставлять фактические аргументы вызова независимо от того, были ли они переданы позиционно или по имени:
>>> def f(a, b, c): pass ... >>> mock = Mock(spec=f) >>> mock(1, 2, c=3) <Mock name='mock()' id='140161580456576'> >>> mock.assert_called_with(1, 2, 3) >>> mock.assert_called_with(a=1, b=2, c=3)
Это относится к
assert_called_with(),assert_called_once_with(),assert_has_calls()иassert_any_call(). При Автоспецификации это также будет относиться к вызовам методов объекта-модели.Изменено в версии 3.4: Добавлена интроспекция подписи для специфицированных и автоспецифицированных объектов-моделей.
-
-
class unittest.mock.PropertyMock(*args, **kwargs) -
Заглушка, предназначенная для использования в качестве свойства (
property) или другого дескриптора в классе.PropertyMockпредоставляет методы__get__()и__set__(), чтобы вы могли указать возвращаемое значение при получении.Получение экземпляра
PropertyMockиз объекта вызывает заглушку без аргументов. Установка вызывает заглушку с устанавливаемым значением.>>> class Foo: ... @property ... def foo(self): ... return 'something' ... @foo.setter ... def foo(self, value): ... pass ... >>> with patch('__main__.Foo.foo', new_callable=PropertyMock) as mock_foo: ... mock_foo.return_value = 'mockity-mock' ... this_foo = Foo() ... print(this_foo.foo) ... this_foo.foo = 6 ... mockity-mock >>> mock_foo.mock_calls [call(), call(6)]
Из-за способа хранения атрибутов заглушек вы не можете напрямую прикрепить PropertyMock к объекту заглушки. Вместо этого вы можете прикрепить его к объекту типа заглушки:
>>> m = MagicMock() >>> p = PropertyMock(return_value=3) >>> type(m).foo = p >>> m.foo 3 >>> p.assert_called_once_with()
Внимание
Если AttributeError возникает в PropertyMock, он будет интерпретироваться как недостающий дескриптор и будет вызван __getattr__() на родительской заглушке:
>>> m = MagicMock() >>> no_attribute = PropertyMock(side_effect=AttributeError) >>> type(m).my_property = no_attribute >>> m.my_property <MagicMock name='mock.my_property' id='140165240345424'>
См. __getattr__() для получения подробностей.
-
class unittest.mock.AsyncMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs) -
Асинхронная версия
MagicMock. ОбъектAsyncMockбудет вести себя так, что объект распознается как асинхронная функция, а результат вызова является асинхронным объектом.>>> mock = AsyncMock() >>> asyncio.iscoroutinefunction(mock) True >>> inspect.isawaitable(mock()) True
Результат
mock()— это асинхронная функция, которая будет иметь результатside_effectилиreturn_valueпосле ожидания:- если
side_effectявляется функцией, асинхронная функция вернёт результат этой функции, - если
side_effectявляется исключением, асинхронная функция вызовет это исключение, - если
side_effectявляется итерируемым объектом, асинхронная функция вернёт следующее значение из итератора. Если последовательность результатов исчерпана,StopAsyncIterationбудет поднято немедленно, - если
side_effectне определено, асинхронная функция вернёт значение, определённоеreturn_value, таким образом, по умолчанию асинхронная функция возвращает новый объектAsyncMock.
Установка параметра spec заглушки
MockилиMagicMockна асинхронную функцию приведёт к возвращению объекта корутины после вызова.>>> async def async_func(): pass ... >>> mock = MagicMock(async_func) >>> mock <MagicMock spec='function' id='...'> >>> mock() <coroutine object AsyncMockMixin._mock_call at ...>
Установка параметра spec для
Mock,MagicMockилиAsyncMockна класс с асинхронными и синхронными функциями автоматически обнаружит синхронные функции и установит их какMagicMock(если родительская заглушка являетсяAsyncMockилиMagicMock) илиMock(если родительская заглушка являетсяMock). Все асинхронные функции будутAsyncMock.>>> class ExampleClass: ... def sync_foo(): ... pass ... async def async_foo(): ... pass ... >>> a_mock = AsyncMock(ExampleClass) >>> a_mock.sync_foo <MagicMock name='mock.sync_foo' id='...'> >>> a_mock.async_foo <AsyncMock name='mock.async_foo' id='...'> >>> mock = Mock(ExampleClass) >>> mock.sync_foo <Mock name='mock.sync_foo' id='...'> >>> mock.async_foo <AsyncMock name='mock.async_foo' id='...'>
Добавлен в версии 3.8.
-
assert_awaited() -
Проверить, что заглушка была ожидана хотя бы один раз. Обратите внимание, что это отличается от того, что объект был вызван, необходимо использовать ключевое слово
await:>>> mock = AsyncMock() >>> async def main(coroutine_mock): ... await coroutine_mock ... >>> coroutine_mock = mock() >>> mock.called True >>> mock.assert_awaited() Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited. >>> asyncio.run(main(coroutine_mock)) >>> mock.assert_awaited()
-
assert_awaited_once() -
Проверить, что заглушка была ожидана ровно один раз.
>>> mock = AsyncMock() >>> async def main(): ... await mock() ... >>> asyncio.run(main()) >>> mock.assert_awaited_once() >>> asyncio.run(main()) >>> mock.method.assert_awaited_once() Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited once. Awaited 2 times.
-
assert_awaited_with(*args, **kwargs) -
Проверить, что последнее ожидание было с указанными аргументами.
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_with('foo', bar='bar') >>> mock.assert_awaited_with('other') Traceback (most recent call last): ... AssertionError: expected call not found. Expected: mock('other') Actual: mock('foo', bar='bar')
-
assert_awaited_once_with(*args, **kwargs) -
Проверить, что заглушка была ожидана ровно один раз и с указанными аргументами.
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_once_with('foo', bar='bar') >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_once_with('foo', bar='bar') Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited once. Awaited 2 times.
-
assert_any_await(*args, **kwargs) -
Проверить, что заглушка была ожидана хотя бы один раз с указанными аргументами.
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> asyncio.run(main('hello')) >>> mock.assert_any_await('foo', bar='bar') >>> mock.assert_any_await('other') Traceback (most recent call last): ... AssertionError: mock('other') await not found
-
assert_has_awaits(calls, any_order=False) -
Проверить, что заглушка была ожидана с указанными вызовами. Список
await_args_listпроверяется на наличие ожиданий.Если any_order ложно, то ожидания должны быть последовательными. До или после указанных ожиданий могут быть дополнительные вызовы.
Если any_order истинно, то ожидания могут быть в любом порядке, но они должны все присутствовать в
await_args_list.>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> calls = [call("foo"), call("bar")] >>> mock.assert_has_awaits(calls) Traceback (most recent call last): ... AssertionError: Awaits not found. Expected: [call('foo'), call('bar')] Actual: [] >>> asyncio.run(main('foo')) >>> asyncio.run(main('bar')) >>> mock.assert_has_awaits(calls)
-
assert_not_awaited() -
Проверить, что заглушка не была ожидана ни разу.
>>> mock = AsyncMock() >>> mock.assert_not_awaited()
-
reset_mock(*args, **kwargs) -
См.
Mock.reset_mock(). Также устанавливаетawait_countв 0,await_argsв None и очищаетawait_args_list.
-
await_count -
Целое число, отслеживающее, сколько раз объект заглушки был ожидаем.
>>> mock = AsyncMock() >>> async def main(): ... await mock() ... >>> asyncio.run(main()) >>> mock.await_count 1 >>> asyncio.run(main()) >>> mock.await_count 2
-
await_args -
Это либо
None(если заглушка не была ожидана), либо аргументы, с которыми заглушка была в последнюю очередь ожидана. Функционально аналогичноMock.call_args.>>> mock = AsyncMock() >>> async def main(*args): ... await mock(*args) ... >>> mock.await_args >>> asyncio.run(main('foo')) >>> mock.await_args call('foo') >>> asyncio.run(main('bar')) >>> mock.await_args call('bar')
-
await_args_list -
Это список всех ожиданий, выполненных для объекта заглушки в последовательности (так что длина списка соответствует количеству ожиданий). До выполнения ожиданий это пустой список.
>>> mock = AsyncMock() >>> async def main(*args): ... await mock(*args) ... >>> mock.await_args_list [] >>> asyncio.run(main('foo')) >>> mock.await_args_list [call('foo')] >>> asyncio.run(main('bar')) >>> mock.await_args_list [call('foo'), call('bar')]
- если
Вызов
Объекты Mock вызываемы. Вызов вернёт значение, установленное как атрибут return_value. Значение по умолчанию — новый объект Mock; он создаётся в первый раз, когда значение возврата обращается (явно или при вызове Mock) — но оно сохраняется и возвращается каждый раз.
Вызовы объекта будут записаны в атрибуты, такие как call_args и call_args_list.
Если установлен атрибут side_effect, он будет вызван после того, как вызов будет записан, поэтому если side_effect вызывает исключение, вызов всё равно записывается.
Самый простой способ сделать так, чтобы вызов Mock вызывал исключение, заключается в том, чтобы сделать side_effect классом исключения или его экземпляром:
>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]
Если side_effect является функцией, то то, что эта функция возвращает, и будет возвращать вызов Mock. Функция side_effect вызывается с теми же аргументами, что и Mock. Это позволяет динамически изменять возвращаемое значение вызова в зависимости от входных данных:
>>> def side_effect(value): ... return value + 1 ... >>> m = MagicMock(side_effect=side_effect) >>> m(1) 2 >>> m(2) 3 >>> m.mock_calls [call(1), call(2)]
Если вы хотите, чтобы Mock всё равно возвращал значение по умолчанию (новый mock) или любое установленное возвращаемое значение, есть два способа. Либо верните mock.return_value изнутри side_effect, либо верните DEFAULT:
>>> m = MagicMock() >>> def side_effect(*args, **kwargs): ... return m.return_value ... >>> m.side_effect = side_effect >>> m.return_value = 3 >>> m() 3 >>> def side_effect(*args, **kwargs): ... return DEFAULT ... >>> m.side_effect = side_effect >>> m() 3
Чтобы удалить side_effect, и вернуться к поведению по умолчанию, установите side_effect в None:
>>> m = MagicMock(return_value=6) >>> def side_effect(*args, **kwargs): ... return 3 ... >>> m.side_effect = side_effect >>> m() 3 >>> m.side_effect = None >>> m() 6
side_effect также может быть любым итерируемым объектом. Повторные вызовы Mock будут возвращать значения из итерируемого объекта (пока итерируемый объект не исчерпан и не возникает StopIteration):
>>> m = MagicMock(side_effect=[1, 2, 3]) >>> m() 1 >>> m() 2 >>> m() 3 >>> m() Traceback (most recent call last): ... StopIteration
Если какие-либо члены итерируемого объекта являются исключениями, они будут подняты вместо возвращаемых значений:
>>> iterable = (33, ValueError, 66) >>> m = MagicMock(side_effect=iterable) >>> m() 33 >>> m() Traceback (most recent call last): ... ValueError >>> m() 66
Удаление атрибутов
Объекты Mock создают атрибуты по мере необходимости. Это позволяет им имитировать объекты любого типа.
Вам может потребоваться, чтобы объект mock возвращал False вызову hasattr() или поднимал AttributeError при получении атрибута. Это можно сделать, предоставив объект в качестве spec для mock, но это не всегда удобно.
Вы «блокируете» атрибуты, удаляя их. После удаления обращение к атрибуту вызовет AttributeError.
>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
...
AttributeError: f
Имена Mock и атрибут name
Поскольку «name» — аргумент конструктора Mock, если вы хотите, чтобы ваш объект mock имел атрибут «name», вы не можете просто передать его при создании. Есть два варианта. Один вариант — использовать configure_mock():
>>> mock = MagicMock() >>> mock.configure_mock(name='my_name') >>> mock.name 'my_name'
Более простой вариант — просто установить атрибут «name» после создания mock:
>>> mock = MagicMock() >>> mock.name = "foo"
Прикрепление Mock в качестве атрибутов
Когда вы прикрепляете mock как атрибут другого mock (или как возвращаемое значение), он становится «потомком» этого mock. Вызовы потомка записываются в атрибутах method_calls и mock_calls родителя. Это полезно для настройки дочерних mock и их прикрепления к родителю, или для прикрепления mock к родителю, который записывает все вызовы потомков и позволяет делать утверждения об порядке вызовов между mock:
>>> parent = MagicMock() >>> child1 = MagicMock(return_value=None) >>> child2 = MagicMock(return_value=None) >>> parent.child1 = child1 >>> parent.child2 = child2 >>> child1(1) >>> child2(2) >>> parent.mock_calls [call.child1(1), call.child2(2)]
Исключением является случай, когда mock имеет имя. Это позволяет предотвратить «родительские» отношения, если по какой-либо причине этого не нужно.
>>> mock = MagicMock() >>> not_a_child = MagicMock(name='not-a-child') >>> mock.attribute = not_a_child >>> mock.attribute() <MagicMock name='not-a-child()' id='...'> >>> mock.mock_calls []
Созданные для вас mock с помощью patch() автоматически получают имена. Чтобы прикрепить mock с именами к родителю, используйте метод attach_mock():
>>> thing1 = object()
>>> thing2 = object()
>>> parent = MagicMock()
>>> with patch('__main__.thing1', return_value=None) as child1:
... with patch('__main__.thing2', return_value=None) as child2:
... parent.attach_mock(child1, 'child1')
... parent.attach_mock(child2, 'child2')
... child1('one')
... child2('two')
...
>>> parent.mock_calls
[call.child1('one'), call.child2('two')]
Декораторы патчинга
Декораторы патчинга используются для патчинга объектов только в пределах области функции, которую они декорируют. Они автоматически обрабатывают отпатчивание, даже если возникают исключения. Все эти функции также могут использоваться в операторе with или в качестве декораторов класса.
patch
Примечание
Ключевым моментом является патчинг в правильном пространстве имён. См. раздел где производить патчинг.
-
unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs) -
patch()выступает в роли декоратора функции, декоратора класса или менеджера контекста. Внутри тела функции или оператора with, target заменяется на new объект. При выходе из функции/оператора with, патчинг отменяется.Если new опущено, то target заменяется на
AsyncMock, если патчится асинхронная функция, или наMagicMockв противном случае. Еслиpatch()используется как декоратор, и new опущено, созданный mock передаётся в качестве дополнительного аргумента декорируемой функции. Еслиpatch()используется как менеджер контекста, созданный mock возвращается менеджером контекста.target должен быть строкой в формате
'package.module.ClassName'. target импортируется, и указанный объект заменяется на new объект, поэтому target должен быть импортируем из среды, из которой вы вызываетеpatch(). target импортируется во время выполнения декорируемой функции, а не во время декорирования.Ключевые аргументы spec и spec_set передаются в
MagicMock, если patch создаёт его для вас.Кроме того, вы можете передать
spec=Trueилиspec_set=True, что заставляет patch передавать мокируемый объект как объект spec/spec_set.new_callable позволяет указать другой класс или вызываемый объект, который будет вызван для создания new объекта. По умолчанию
AsyncMockиспользуется для асинхронных функций иMagicMockдля остальных.Более мощная форма spec — autospec. Если вы установите
autospec=True, то mock будет создан с спецификацией из заменяемого объекта. Все атрибуты mock также будут иметь спецификацию соответствующего атрибута заменяемого объекта. Методы и функции, подвергаемые мокированию, будут проверять аргументы и вызыватьTypeError, если они вызваны с неправильной сигнатурой. Для mocks, заменяющих класс, их возвращаемое значение («экземпляр») будет иметь ту же спецификацию, что и класс. См. функциюcreate_autospec()и Автоспецификация.Вместо
autospec=Trueвы можете передатьautospec=some_objectдля использования произвольного объекта в качестве спецификации вместо заменяемого.По умолчанию
patch()не сможет заменить атрибуты, которых нет. Если вы передадитеcreate=True, и атрибут не существует, patch создаст атрибут для вас при вызове запатченной функции и удалит его после выхода из запатченной функции. Это полезно для написания тестов на атрибуты, которые ваш производственный код создаёт во время выполнения. По умолчанию эта функция выключена, так как может быть опасной. Включив её, вы можете писать проходящие тесты для API, которые фактически не существуют!Примечание
Изменено в версии 3.5: Если вы патчите встроенные функции в модуле, то вам не нужно передавать
create=True, он будет добавлен по умолчанию.Patch может использоваться как декоратор класса
TestCase. Он работает путём декорации каждого тестового метода в классе. Это сокращает объём кода, когда ваши тестовые методы используют общий набор патчинга.patch()находит тесты, ища имена методов, начинающиеся сpatch.TEST_PREFIX. По умолчанию это'test', что соответствует способу нахождения тестовunittest. Вы можете указать альтернативный префикс, установивpatch.TEST_PREFIX.Patch может использоваться как менеджер контекста с оператором with. Здесь патчинг применяется к отстуженному блоку после оператора with. Если вы используете «as», то запатченный объект будет привязан к имени после «as»; очень полезно, если
patch()создаёт mock объект для вас.patch()принимает произвольные ключевые аргументы. Они будут переданы вAsyncMock, если запатченный объект является асинхронным, вMagicMockв противном случае или в new_callable, если указан.patch.dict(...),patch.multiple(...)иpatch.object(...)доступны для альтернативных вариантов использования.
patch() как декоратор функции, создавая mock для вас и передавая его в декорируемую функцию:
>>> @patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
... print(mock_class is SomeClass)
...
>>> function(None)
True
Патчинг класса заменяет класс экземпляром MagicMock. Если класс инициализируется в тестируемом коде, то это будет return_value mock, который будет использоваться.
Если класс инициализируется несколько раз, вы можете использовать side_effect для возврата нового mock каждый раз. В качестве альтернативы вы можете установить return_value на любое желаемое значение.
Для настройки возвращаемых значений методов экземпляров в запатченном классе вы должны это сделать в return_value. Например:
>>> class Class:
... def method(self):
... pass
...
>>> with patch('__main__.Class') as MockClass:
... instance = MockClass.return_value
... instance.method.return_value = 'foo'
... assert Class() is instance
... assert Class().method() == 'foo'
...
Если вы используете spec или spec_set и patch() заменяет класс, то возвращаемое значение созданного mock будет иметь ту же спецификацию.
>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()
Аргумент new_callable полезен, когда вы хотите использовать другой класс вместо значения по умолчанию MagicMock для созданного mock. Например, если вы хотите использовать NonCallableMock:
>>> thing = object()
>>> with patch('__main__.thing', new_callable=NonCallableMock) as mock_thing:
... assert thing is mock_thing
... thing()
...
Traceback (most recent call last):
...
TypeError: 'NonCallableMock' object is not callable
Другой вариант использования — замена объекта экземпляром io.StringIO:
>>> from io import StringIO
>>> def foo():
... print('Something')
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
... foo()
... assert mock_stdout.getvalue() == 'Something\n'
...
>>> test()
Когда patch() создаёт mock для вас, обычно первым делом вам нужно настроить mock. Часть этой настройки может быть выполнена в вызове patch. Любые произвольные ключевые слова, которые вы передаёте в вызов, будут использованы для установки атрибутов на созданном mock:
>>> patcher = patch('__main__.thing', first='one', second='two')
>>> mock_thing = patcher.start()
>>> mock_thing.first
'one'
>>> mock_thing.second
'two'
Помимо атрибутов на созданном mock, а также атрибуты, такие как return_value и side_effect, дочерних mock также могут быть настроены. Они не являются синтаксически допустимыми для передачи непосредственно в качестве ключевых аргументов, но словарь с этими ключами всё равно может быть расширен в вызов patch() с использованием **.
>>> config = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> patcher = patch('__main__.thing', **config)
>>> mock_thing = patcher.start()
>>> mock_thing.method()
3
>>> mock_thing.other()
Traceback (most recent call last):
...
KeyError
По умолчанию попытка патчить функцию в модуле (или метод или атрибут в классе), которой не существует, завершится с AttributeError:
>>> @patch('sys.non_existing_attribute', 42)
... def test():
... assert sys.non_existing_attribute == 42
...
>>> test()
Traceback (most recent call last):
...
AttributeError: <module 'sys' (built-in)> does not have the attribute 'non_existing_attribute'
но добавление create=True в вызов patch() сделает предыдущий пример рабочим:
>>> @patch('sys.non_existing_attribute', 42, create=True)
... def test(mock_stdout):
... assert sys.non_existing_attribute == 42
...
>>> test()
patch.object
-
patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs) -
заменяет указанный член (атрибут) объекта (target) на объект-заглушку.
patch.object()может использоваться как декоратор, декоратор класса или менеджер контекста. Аргументы new, spec, create, spec_set, autospec и new_callable имеют то же значение, что и дляpatch(). Как иpatch(),patch.object()принимает произвольные ключевые аргументы для настройки создаваемого объекта-заглушки.При использовании в качестве декоратора класса
patch.object()учитываетpatch.TEST_PREFIXдля выбора методов, которые следует обернуть.
Вы можете вызвать patch.object() с тремя аргументами или двумя аргументами. Форма с тремя аргументами принимает объект для замены, имя атрибута и объект, который должен заменить атрибут.
При вызове с двумя аргументами вы опускаете замещающий объект, и для вас создаётся заглушка, которая передаётся в качестве дополнительного аргумента в декорированную функцию:
>>> @patch.object(SomeClass, 'class_method') ... def test(mock_method): ... SomeClass.class_method(3) ... mock_method.assert_called_with(3) ... >>> test()
spec, create и другие аргументы patch.object() имеют то же значение, что и для patch().
patch.dict
-
patch.dict(in_dict, values=(), clear=False, **kwargs) -
Заменяет словарь или подобный ему объект и восстанавливает словарь до его первоначального состояния после теста.
in_dict может быть словарем или контейнером, подобным отображению. Если это отображение, то оно должно по крайней мере поддерживать получение, установку и удаление элементов, а также итерацию по ключам.
in_dict также может быть строкой, задающей имя словаря, который затем будет получен импортом.
values может быть словарем значений для установки в словарь. values также может быть итерируемым списком
(key, value)пар.Если clear равно true, то словарь будет очищен перед установкой новых значений.
patch.dict()также может быть вызван с произвольными ключевыми аргументами для установки значений в словарь.Изменено в версии 3.8:
patch.dict()теперь возвращает изменённый словарь при использовании в качестве менеджера контекста.
patch.dict() может использоваться в качестве менеджера контекста, декоратора или декоратора класса:
>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
... assert foo == {'newkey': 'newvalue'}
...
>>> test()
>>> assert foo == {}
При использовании в качестве декоратора класса patch.dict() учитывает patch.TEST_PREFIX (по умолчанию 'test') для выбора методов, которые следует обернуть:
>>> import os
>>> import unittest
>>> from unittest.mock import patch
>>> @patch.dict('os.environ', {'newkey': 'newvalue'})
... class TestSample(unittest.TestCase):
... def test_sample(self):
... self.assertEqual(os.environ['newkey'], 'newvalue')
Если вы хотите использовать другой префикс для своих тестов, вы можете сообщить об этом патчеру, задав patch.TEST_PREFIX. Дополнительные сведения о том, как изменить значение, см. в TEST_PREFIX.
patch.dict() может использоваться для добавления членов в словарь или просто для того, чтобы тест изменял словарь, и гарантирует, что словарь будет восстановлен по окончании теста.
>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
... assert foo == {'newkey': 'newvalue'}
... assert patched_foo == {'newkey': 'newvalue'}
... # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
... patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
... print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ
Ключевые слова могут быть использованы в вызове patch.dict() для установки значений в словарь:
>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
... import mymodule
... mymodule.function('some', 'args')
...
'fish'
patch.dict() может использоваться с объектами, похожими на словарь, которые фактически не являются словарями. По меньшей мере, они должны поддерживать получение, установку, удаление элементов и либо итерацию, либо проверку принадлежности. Это соответствует магическим методам __getitem__(), __setitem__(), __delitem__() и либо __iter__(), либо __contains__().
>>> class Container:
... def __init__(self):
... self.values = {}
... def __getitem__(self, name):
... return self.values[name]
... def __setitem__(self, name, value):
... self.values[name] = value
... def __delitem__(self, name):
... del self.values[name]
... def __iter__(self):
... return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
... assert thing['one'] == 2
... assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']
patch.multiple
-
patch.multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs) -
Производит несколько замещений в одном вызове. Принимает объект для замены (либо как объект, либо как строку для получения объекта путём импорта) и ключевые аргументы для замещений:
with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): ...Используйте
DEFAULTв качестве значения, если вы хотите, чтобыpatch.multiple()создавал для вас заглушки. В этом случае созданные заглушки передаются в декорированную функцию по ключу, а словарь возвращается при использованииpatch.multiple()как менеджера контекста.patch.multiple()может использоваться как декоратор, декоратор класса или менеджер контекста. Аргументы spec, spec_set, create, autospec и new_callable имеют то же значение, что и дляpatch(). Эти аргументы будут применяться ко всем замещениям, выполненнымpatch.multiple().При использовании в качестве декоратора класса
patch.multiple()учитываетpatch.TEST_PREFIXдля выбора методов, которые следует обернуть.
Если вы хотите, чтобы patch.multiple() создавал заглушки для вас, то вы можете использовать DEFAULT в качестве значения. Если вы используете patch.multiple() как декоратор, то созданные заглушки передаются в декорированную функцию по ключу.
>>> thing = object()
>>> other = object()
>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
... assert isinstance(thing, MagicMock)
... assert isinstance(other, MagicMock)
...
>>> test_function()
patch.multiple() может быть вложенным с другими patch декораторами, но аргументы, передаваемые по ключу, должны располагаться после любых стандартных аргументов, созданных patch():
>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
... assert 'other' in repr(other)
... assert 'thing' in repr(thing)
... assert 'exit' in repr(mock_exit)
...
>>> test_function()
Если patch.multiple() используется как менеджер контекста, значение, возвращаемое менеджером контекста, — словарь, где созданные заглушки имеют ключи по имени:
>>> with patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) as values:
... assert 'other' in repr(values['other'])
... assert 'thing' in repr(values['thing'])
... assert values['thing'] is thing
... assert values['other'] is other
...
методы патчей: start и stop
Все патчеры имеют методы start() и stop(). Они упрощают выполнение патчинга в методах setUp или там, где требуется выполнить несколько замещений без вложенных декораторов или инструкций с оператором «with».
Для их использования вызовите patch(), patch.object() или patch.dict() как обычно и сохраните ссылку на возвращаемый patcher объект. Затем вы можете вызвать start() для размещения патча и stop() для его отмены.
Если вы используете patch() для создания заглушки, то она будет возвращена в результате вызова patcher.start.
>>> patcher = patch('package.module.ClassName')
>>> from package import module
>>> original = module.ClassName
>>> new_mock = patcher.start()
>>> assert module.ClassName is not original
>>> assert module.ClassName is new_mock
>>> patcher.stop()
>>> assert module.ClassName is original
>>> assert module.ClassName is not new_mock
Типичный пример использования этого подхода — выполнение нескольких замещений в методе setUp объекта TestCase.
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... self.patcher1 = patch('package.module.Class1')
... self.patcher2 = patch('package.module.Class2')
... self.MockClass1 = self.patcher1.start()
... self.MockClass2 = self.patcher2.start()
...
... def tearDown(self):
... self.patcher1.stop()
... self.patcher2.stop()
...
... def test_something(self):
... assert package.module.Class1 is self.MockClass1
... assert package.module.Class2 is self.MockClass2
...
>>> MyTest('test_something').run()
Внимание
Если вы используете этот приём, вы должны гарантировать, что патчинг «отменён» вызовом stop. Это может быть сложнее, чем вы ожидаете, поскольку если в методе setUp возникает исключение, то tearDown не вызывается. unittest.TestCase.addCleanup() упрощает это:
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... patcher = patch('package.module.Class')
... self.MockClass = patcher.start()
... self.addCleanup(patcher.stop)
...
... def test_something(self):
... assert package.module.Class is self.MockClass
...
В качестве дополнительного преимущества вам больше не нужно хранить ссылку на patcher объект.
Также можно остановить все начатые замещения, используя patch.stopall().
-
patch.stopall() -
Останавливает все активные замещения. Останавливает только замещения, начатые с
start.
patch builtins
Вы можете заменить любые встроенные функции в модуле. Следующий пример заменяет встроенную функцию ord():
>>> @patch('__main__.ord')
... def test(mock_ord):
... mock_ord.return_value = 101
... print(ord('c'))
...
>>> test()
101
ПРЕФИКС_ТЕСТА
Все патчеры могут использоваться в качестве декораторов классов. При использовании таким образом, они обертывают каждый тестовый метод в классе. Патчеры распознают методы, начинающиеся с 'test' как тестовые методы. Это тот же способ, которым unittest.TestLoader находит тестовые методы по умолчанию.
Возможно, вы хотите использовать другой префикс для своих тестов. Вы можете сообщить патчерам о другом префиксе, установив patch.TEST_PREFIX:
>>> patch.TEST_PREFIX = 'foo'
>>> value = 3
>>>
>>> @patch('__main__.value', 'not three')
... class Thing:
... def foo_one(self):
... print(value)
... def foo_two(self):
... print(value)
...
>>>
>>> Thing().foo_one()
not three
>>> Thing().foo_two()
not three
>>> value
3
Вложенные декораторы патчинга
Если вы хотите выполнить несколько патчей, то вы можете просто указать декораторы.
Вы можете указать несколько декораторов патчинга, используя этот шаблон:
>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
... assert SomeClass.static_method is mock1
... assert SomeClass.class_method is mock2
... SomeClass.static_method('foo')
... SomeClass.class_method('bar')
... return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')
Обратите внимание, что декораторы применяются снизу вверх. Это стандартный способ, которым Python применяет декораторы. Порядок созданных моков, переданных в вашу тестовую функцию, соответствует этому порядку.
Где производить патчинг
patch() работает, временно изменяя объект, на который указывает имя, на другой. Может быть много имён, указывающих на любой отдельный объект, поэтому для работы патчинга необходимо убедиться, что вы производите патчинг имени, используемого системой, которую вы тестируете.
Основной принцип состоит в том, что вы производите патчинг там, где ищется объект, что необязательно совпадает с местом его определения. Несколько примеров помогут прояснить это.
Представьте, что у нас есть проект, который мы хотим протестировать со следующей структурой:
a.py
-> Defines SomeClass
b.py
-> from a import SomeClass
-> some_function instantiates SomeClass
Теперь мы хотим протестировать some_function, но мы хотим смокировать SomeClass с помощью patch(). Проблема заключается в том, что при импорте модуля b, который нам нужно сделать, он импортирует SomeClass из модуля a. Если мы используем patch() для смокинга a.SomeClass , то это не повлияет на наш тест; модуль b уже имеет ссылку на реальный SomeClass и похоже, что наш патчинг не повлиял.
Ключ заключается в том, чтобы смокировать SomeClass там, где он используется (или где он ищется). В этом случае some_function фактически будет искать SomeClass в модуле b, где мы его импортировали. Патчинг должен выглядеть так:
@patch('b.SomeClass')
Однако, рассмотрим альтернативный сценарий, где вместо SomeClass модуль b производит import a и some_function использует a.SomeClass. Оба этих формата импорта являются распространенными. В этом случае класс, который мы хотим смокировать, ищется в модуле, и поэтому мы должны смокировать a.SomeClass вместо этого:
@patch('a.SomeClass')
Патчинг дескрипторов и прокси-объектов
Как patch, так и patch.object правильно производят патчинг и восстановление дескрипторов: методов класса, статических методов и свойств. Вы должны производить патчинг этих элементов на классе, а не на экземпляре. Они также работают с некоторыми объектами, которые делегируют доступ к атрибутам, например, объектом настроек Django.
Поддержка MagicMock и магических методов
Моделирование магических методов
Mock поддерживает моделирование методов Python-протокола, также известных как “магические методы”. Это позволяет объектам-заглушкам заменять контейнеры или другие объекты, реализующие Python-протоколы.
Поскольку магические методы ищут по-другому, чем обычные методы [2], эта поддержка была реализована специально. Это означает, что поддерживаются только определённые магические методы. Список поддерживаемых методов включает почти все из них. Если есть какие-то отсутствующие, которые вам нужны, сообщите нам, пожалуйста.
Вы моделируете магические методы, назначив интересующий метод функции или экземпляру объекта-заглушки. Если вы используете функцию, она должна принимать self в качестве первого аргумента [3].
>>> def __str__(self): ... return 'fooble' ... >>> mock = Mock() >>> mock.__str__ = __str__ >>> str(mock) 'fooble'
>>> mock = Mock() >>> mock.__str__ = Mock() >>> mock.__str__.return_value = 'fooble' >>> str(mock) 'fooble'
>>> mock = Mock() >>> mock.__iter__ = Mock(return_value=iter([])) >>> list(mock) []
Один из вариантов использования этого — для моделирования объектов, используемых в качестве менеджеров контекста в операторе with:
>>> mock = Mock() >>> mock.__enter__ = Mock(return_value='foo') >>> mock.__exit__ = Mock(return_value=False) >>> with mock as m: ... assert m == 'foo' ... >>> mock.__enter__.assert_called_with() >>> mock.__exit__.assert_called_with(None, None, None)
Вызовы магических методов не отображаются в method_calls, но записываются в mock_calls.
Примечание
Если вы используете ключевое слово spec для создания объекта-заглушки, попытка установить магический метод, который не указан в спецификации, вызовет AttributeError.
Полный список поддерживаемых магических методов:
-
__hash__,__sizeof__,__repr__и__str__ -
__dir__,__format__и__subclasses__ -
__round__,__floor__,__trunc__и__ceil__ - Сравнения:
__lt__,__gt__,__le__,__ge__,__eq__и__ne__ - Методы контейнеров:
__getitem__,__setitem__,__delitem__,__contains__,__len__,__iter__,__reversed__и__missing__ - Менеджер контекста:
__enter__,__exit__,__aenter__и__aexit__ - Унарные числовые методы:
__neg__,__pos__и__invert__ - Числовые методы (включая правые и ин-плейс варианты):
__add__,__sub__,__mul__,__matmul__,__truediv__,__floordiv__,__mod__,__divmod__,__lshift__,__rshift__,__and__,__xor__,__or__, и__pow__ - Методы числового преобразования:
__complex__,__int__,__float__и__index__ - Методы дескрипторов:
__get__,__set__и__delete__ - Пиклирование:
__reduce__,__reduce_ex__,__getinitargs__,__getnewargs__,__getstate__и__setstate__ - Представление пути файловой системы:
__fspath__ - Асинхронные методы итерации:
__aiter__и__anext__
Изменено в версии 3.8: Добавлена поддержка os.PathLike.__fspath__().
Изменено в версии 3.8: Добавлена поддержка __aenter__, __aexit__, __aiter__ и __anext__.
Следующие методы существуют, но не поддерживаются, так как они либо используются mock, либо не могут быть динамически заданы, либо могут вызвать проблемы:
-
__getattr__,__setattr__,__init__и__new__ -
__prepare__,__instancecheck__,__subclasscheck__,__del__
Magic Mock
Существует два MagicMock варианта: MagicMock и NonCallableMagicMock.
-
class unittest.mock.MagicMock(*args, **kw) -
MagicMock— подклассMockс предопределёнными реализациями большинства магических методов. Вы можете использоватьMagicMockбез необходимости самостоятельной настройки магических методов.Параметры конструктора имеют такое же значение, как и для
Mock.Если вы используете аргументы spec или spec_set, то будут созданы только магические методы, присутствующие в спецификации.
-
class unittest.mock.NonCallableMagicMock(*args, **kw) -
Невызываемый вариант
MagicMock.Параметры конструктора имеют такое же значение, как и для
MagicMock, за исключением return_value и side_effect, которые не имеют смысла для невызываемой заглушки.
Магические методы настраиваются с помощью объектов MagicMock, поэтому вы можете настроить их и использовать обычным способом:
>>> mock = MagicMock() >>> mock[3] = 'fish' >>> mock.__setitem__.assert_called_with(3, 'fish') >>> mock.__getitem__.return_value = 'result' >>> mock[2] 'result'
По умолчанию многие методы протокола должны возвращать объекты определённого типа. Эти методы предварительно настроены на возвращаемое значение по умолчанию, чтобы их можно было использовать без каких-либо действий, если возвращаемое значение вас не интересует. Вы по-прежнему можете изменить возвращаемое значение вручную, если хотите.
Методы и их значения по умолчанию:
-
__lt__:NotImplemented -
__gt__:NotImplemented -
__le__:NotImplemented -
__ge__:NotImplemented -
__int__:1 -
__contains__:False -
__len__:0 -
__iter__:iter([]) -
__exit__:False -
__aexit__:False -
__complex__:1j -
__float__:1.0 -
__bool__:True -
__index__:1 -
__hash__: хэш по умолчанию для объекта-заглушки -
__str__: строка по умолчанию для объекта-заглушки -
__sizeof__: размер по умолчанию для объекта-заглушки
Например:
>>> mock = MagicMock() >>> int(mock) 1 >>> len(mock) 0 >>> list(mock) [] >>> object() in mock False
Два метода равенства, __eq__() и __ne__(), являются специальными. Они выполняют стандартное сравнение по идентичности, используя атрибут side_effect, если вы не измените их возвращаемое значение на что-то другое:
>>> MagicMock() == 3 False >>> MagicMock() != 3 True >>> mock = MagicMock() >>> mock.__eq__.return_value = True >>> mock == 3 True
Возвращаемое значение MagicMock.__iter__() может быть любым итерируемым объектом и не обязательно итератором:
>>> mock = MagicMock() >>> mock.__iter__.return_value = ['a', 'b', 'c'] >>> list(mock) ['a', 'b', 'c'] >>> list(mock) ['a', 'b', 'c']
Если возвращаемое значение является итератором, то один раз пройдя по нему, он будет исчерпан, а последующие итерации вернут пустой список:
>>> mock.__iter__.return_value = iter(['a', 'b', 'c']) >>> list(mock) ['a', 'b', 'c'] >>> list(mock) []
MagicMock содержит все поддерживаемые магические методы, за исключением некоторых устаревших и малоизвестных. Вы по-прежнему можете настроить их, если хотите.
Магические методы, которые поддерживаются, но по умолчанию не настроены в MagicMock:
__subclasses____dir____format__-
__get__,__set__и__delete__ -
__reversed__и__missing__ -
__reduce__,__reduce_ex__,__getinitargs__,__getnewargs__,__getstate__и__setstate__ __getformat__
Магические методы должны искоться в классе, а не в экземпляре. Различные версии 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 в качестве своей спецификации.
Функции или методы, которые подделываются, будут проверяться, чтобы убедиться, что они вызываются с правильной сигнатурой.
Если spec_set равно
True, то попытка установить атрибуты, которых нет в объекте spec, вызоветAttributeError.Если в качестве спецификации используется класс, то возвращаемое значение подделки (экземпляр класса) будет иметь ту же спецификацию. Вы можете использовать класс в качестве спецификации для объекта экземпляра, передав
instance=True. Возвращаемая подделка будет вызываемой только в том случае, если экземпляры подделки вызываемы.create_autospec()также принимает произвольные аргументы ключевых слов, которые передаются в конструктор созданной подделки.
См. Автоспецификацию для примеров использования автоспецификации с create_autospec() и аргументом autospec для patch().
Изменено в версии 3.8: create_autospec() теперь возвращает AsyncMock, если целевая функция является асинхронной.
ANY
-
unittest.mock.ANY
Иногда может потребоваться сделать утверждения о некоторых аргументах в вызове подделки, но либо не заботиться о некоторых аргументах, либо вы хотите извлечь их по отдельности из call_args и сделать более сложные утверждения о них.
Чтобы игнорировать определенные аргументы, вы можете передать объекты, которые сравниваются со всем. Вызовы assert_called_with() и assert_called_once_with() будут выполняться успешно независимо от того, что было передано.
>>> mock = Mock(return_value=None)
>>> mock('foo', bar=object())
>>> mock.assert_called_once_with('foo', bar=ANY)
ANY также можно использовать в сравнениях со списками вызовов, такими как mock_calls:
>>> m = MagicMock(return_value=None) >>> m(1) >>> m(1, 2) >>> m(object()) >>> m.mock_calls == [call(1), call(1, 2), ANY] True
ANY не ограничивается сравнениями с объектами вызовов и поэтому также может использоваться в тестовых утверждениях:
class TestStringMethods(unittest.TestCase):
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', ANY])
FILTER_DIR
-
unittest.mock.FILTER_DIR
FILTER_DIR — переменная уровня модуля, которая управляет тем, как объекты подделки реагируют на dir(). По умолчанию это True, что использует фильтрацию, описанную ниже, чтобы отображать только полезные члены. Если вы не хотите использовать эту фильтрацию или вам нужно ее отключить для диагностических целей, установите mock.FILTER_DIR = False.
При включенной фильтрации dir(some_mock) отображает только полезные атрибуты и включает все динамически созданные атрибуты, которые обычно не отображаются. Если подделка создавалась со спецификацией (или, конечно, autospec), то отображаются все атрибуты из оригинала, даже если они еще не были обработаны:
>>> dir(Mock()) ['assert_any_call', 'assert_called', 'assert_called_once', 'assert_called_once_with', 'assert_called_with', 'assert_has_calls', 'assert_not_called', 'attach_mock', ... >>> from urllib import request >>> dir(Mock(spec=request)) ['AbstractBasicAuthHandler', 'AbstractDigestAuthHandler', 'AbstractHTTPHandler', 'BaseHandler', ...
Многие не очень полезные (принадлежащие Mock, а не тому, что имитируется) атрибуты с префиксом подчеркивания и двойного подчеркивания были отфильтрованы из результата вызова dir() на Mock. Если вы не хотите этого поведения, вы можете отключить его, установив переключатель уровня модуля FILTER_DIR:
>>> from unittest import mock >>> mock.FILTER_DIR = False >>> dir(mock.Mock()) ['_NonCallableMock__get_return_value', '_NonCallableMock__get_side_effect', '_NonCallableMock__return_value_doc', '_NonCallableMock__set_return_value', '_NonCallableMock__set_side_effect', '__call__', '__class__', ...
В качестве альтернативы можно использовать vars(my_mock) (члены экземпляра) и dir(type(my_mock)) (члены типа), чтобы обойти фильтрацию независимо от mock.FILTER_DIR.
mock_open
-
unittest.mock.mock_open(mock=None, read_data=None) -
Функция-помощник для создания мока, заменяющего использование
open(). Она работает дляopen(), вызываемого напрямую или используемого в качестве менеджера контекста.Аргумент mock — это объект мока для настройки. Если
None(по умолчанию), то для вас будет созданMagicMockс ограниченным API методами или атрибутами, доступными в стандартных файловых обработчиках.read_data — это строка для
read(), методовreadline()иreadlines()файлового обработчика для возвращаемого значения. Вызовы этих методов будут брать данные из read_data до тех пор, пока оно не истощится. Мок этих методов довольно прост: каждый раз, когда вызывается mock, read_data перематывается в начало. Если вам нужен больший контроль над данными, которые вы передаёте тестируемому коду, вам потребуется настроить этот мок самостоятельно. Если этого недостаточно, пакеты системных файлов в памяти на PyPI могут предложить реалистичную файловую систему для тестирования.Изменено в версии 3.4: Добавлена поддержка
readline()иreadlines(). Мокread()изменён для потребления read_data вместо возврата его при каждом вызове.Изменено в версии 3.5: read_data теперь сбрасывается при каждом вызове mock.
Изменено в версии 3.8: В реализацию добавлена
__iter__(), поэтому итерация (например, в циклах for) правильно потребляет read_data.
Использование open() в качестве менеджера контекста — отличный способ обеспечить правильное закрытие файловых обработчиков, и это становится распространённым:
with open('/some/path', 'w') as f:
f.write('something')
Проблема в том, что даже если вы смоделируете вызов open(), именно возвращаемый объект используется как менеджер контекста (и у него вызываются __enter__() и __exit__()).
Моделирование менеджеров контекста с помощью MagicMock достаточно распространено и достаточно сложно, поэтому функция-помощник полезна.
>>> m = mock_open()
>>> with patch('__main__.open', m):
... with open('foo', 'w') as h:
... h.write('some stuff')
...
>>> m.mock_calls
[call('foo', 'w'),
call().__enter__(),
call().write('some stuff'),
call().__exit__(None, None, None)]
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')
А для чтения файлов:
>>> with patch('__main__.open', mock_open(read_data='bibble')) as m:
... with open('foo') as h:
... result = h.read()
...
>>> m.assert_called_once_with('foo')
>>> assert result == 'bibble'
Автоспессинг
Автоспессинг основан на существующей spec функции mock. Он ограничивает API подделок API исходного объекта (спецификации), но он рекурсивен (реализован лениво), так что атрибуты подделок имеют тот же API, что и атрибуты спецификации. Кроме того, поддельные функции/методы имеют такую же сигнатуру вызова, как и оригинальные, поэтому они генерируют TypeError, если они вызываются неправильно.
Прежде чем объяснить, как работает автоспессинг, вот почему он необходим.
Mock — очень мощный и гибкий объект, но у него есть недостаток, общий для подделок. Если вы переименуете часть своего кода, переименуете члены и т. д., любые тесты кода, который все еще использует старый API, но использует подделки вместо реальных объектов, все равно пройдут. Это означает, что ваши тесты могут пройти успешно, даже если ваш код сломан.
Изменено в версии 3.5: До версии 3.5 тесты с ошибкой в слове assert бесшумно проходили, когда должны были генерировать ошибку. Вы по-прежнему можете добиться такого поведения, передав unsafe=True в Mock.
Обратите внимание, что это еще одна причина, почему вам также нужны интеграционные тесты, а не только модульные. Тестирование всего в изоляции — это хорошо и замечательно, но если вы не тестируете, как ваши модули «связаны между собой», всё равно есть много места для ошибок, которые могли бы быть обнаружены тестами.
mock уже предоставляет функцию для решения этой проблемы, которая называется спессинг. Если вы используете класс или экземпляр в качестве spec для подделки, тогда вы можете получить доступ только к атрибутам подделки, которые существуют в реальном классе:
>>> from urllib import request >>> mock = Mock(spec=request.Request) >>> mock.assret_called_with # Intentional typo! Traceback (most recent call last): ... AttributeError: Mock object has no attribute 'assret_called_with'
Спецификация применяется только к самой подделке, поэтому у нас по-прежнему та же проблема с любыми методами подделки:
>>> mock.has_data() <mock.Mock object at 0x...> >>> mock.has_data.assret_called_with() # Intentional typo!
Автоспессинг решает эту проблему. Вы можете либо передать autospec=True в patch() / patch.object(), либо использовать функцию create_autospec() для создания подделки со спецификацией. Если вы используете аргумент autospec=True для patch(), то объект, который заменяется, будет использован как объект спецификации. Поскольку спессинг выполняется «лениво» (спецификация создается при обращении к атрибутам подделки), вы можете использовать его с очень сложными или глубоко вложенными объектами (например, модулями, которые импортируют модули, которые импортируют модули) без значительной потери производительности.
Вот пример его использования:
>>> from urllib import request
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> request is mock_request
True
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>
Вы можете видеть, что request.Request имеет спецификацию. request.Request принимает два аргумента в конструкторе (один из которых self). Вот что произойдёт, если мы попытаемся вызвать его неправильно:
>>> req = request.Request() Traceback (most recent call last): ... TypeError: <lambda>() takes at least 2 arguments (1 given)
Спецификация также применяется к экземплярам классов (т. е. к возвращаемому значению подделок со спецификацией):
>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>
Request объекты не вызываемы, поэтому возвращаемое значение создания нашего подделанного объекта request.Request — это не вызываемая подделка. Благодаря спецификации любые ошибки в наших утверждениях приведут к генерации правильной ошибки:
>>> req.add_header('spam', 'eggs')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_with # Intentional typo!
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')
Во многих случаях вы сможете просто добавить autospec=True к своим существующим вызовам patch(), чтобы защититься от ошибок из-за опечаток и изменений API.
Помимо использования autospec через patch(), существует create_autospec() для непосредственного создания подделок со спецификацией:
>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>
Однако это не лишено нюансов и ограничений, поэтому оно не является стандартным поведением. Чтобы узнать, какие атрибуты доступны в объекте спецификации, autospec должен инспектировать (получать доступ к атрибутам) спецификацию. При прохождении по атрибутам подделки одновременно происходит соответствующее прохождение по исходному объекту. Если какие-либо ваши объекты со спецификацией имеют свойства или дескрипторы, которые могут вызывать выполнение кода, вы можете не иметь возможности использовать autospec. С другой стороны, лучше разработать свои объекты так, чтобы интроспекция была безопасной [4].
Более серьезная проблема заключается в том, что часто атрибуты экземпляров создаются в методе __init__() и вообще не существуют в классе. autospec не может знать о динамически созданных атрибутах и ограничивает API видимыми атрибутами.
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Существует несколько способов решения этой проблемы. Наиболее простой, но не обязательно наименее раздражающий способ — просто установить необходимые атрибуты в подделке после её создания. Просто потому, что autospec не позволяет получать доступ к атрибутам, которые не существуют в спецификации, это не мешает вам их задавать:
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a = 33
...
Существует более агрессивная версия и spec, и autospec, которая препятствует установке несуществующих атрибутов. Это полезно, если вы хотите гарантировать, что ваш код устанавливает только допустимые атрибуты, но, очевидно, это предотвращает этот конкретный сценарий:
>>> with patch('__main__.Something', autospec=True, spec_set=True):
... thing = Something()
... thing.a = 33
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Вероятно, лучший способ решения проблемы — добавить атрибуты класса в качестве значения по умолчанию для членов экземпляра, инициализированных в методе __init__(). Обратите внимание, что если вы устанавливаете только атрибуты по умолчанию в __init__(), то предоставление их через атрибуты класса (общие для всех экземпляров, конечно) также быстрее. Например:
class Something:
a = 33
Это поднимает еще одну проблему. Довольно часто предоставляется значение по умолчанию None для членов, которые впоследствии будут объектом другого типа. None будет бесполезен в качестве спецификации, потому что он не позволит получить доступ к каким-либо атрибутам или методам. Поскольку None никогда не будет полезен в качестве спецификации и, вероятно, указывает на член, который обычно будет какого-то другого типа, autospec не использует спецификацию для членов, установленных в None. Они будут обычными подделками (ну — MagicMock):
>>> class Something: ... member = None ... >>> mock = create_autospec(Something) >>> mock.member.foo.bar.baz() <MagicMock name='mock.member.foo.bar.baz()' id='...'>
Если изменение ваших производственных классов для добавления значений по умолчанию вам не подходит, есть другие варианты. Один из них — просто использовать экземпляр в качестве спецификации, а не класс. Другой — создать подкласс производственного класса и добавить значения по умолчанию в подкласс без влияния на производственный класс. Оба эти варианта требуют использования альтернативного объекта в качестве спецификации. К счастью, patch() поддерживает это — вы можете просто передать альтернативный объект в качестве аргумента autospec:
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> class SomethingForTest(Something):
... a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>
Это относится только к классам или уже созданным объектам. Вызов подделанного класса для создания экземпляра подделки не создаёт реальный экземпляр. Это только поиск атрибутов — наряду с вызовами dir() — которые выполняются.
Запечатывание подделок
-
unittest.mock.seal(mock) -
Запечатывание отключит автоматическое создание подделок при обращении к атрибуту запечатываемой подделки или любому из её атрибутов, которые уже являются рекурсивными подделками.
Если экземпляр подделки с именем или спецификацией присваивается атрибуту, он не будет учитываться в цепочке запечатывания. Это позволяет предотвратить исправление части объекта подделки seal.
>>> mock = Mock() >>> mock.submock.attribute1 = 2 >>> mock.not_submock = mock.Mock(name="sample_name") >>> seal(mock) >>> mock.new_attribute # This will raise AttributeError. >>> mock.submock.attribute2 # This will raise AttributeError. >>> mock.not_submock.attribute2 # This won't raise.
Добавлена в версии 3.7.
Порядок приоритета side_effect, return_value и wraps
Порядок их приоритета:
side_effectreturn_value- wraps
Если все три значения заданы, mock вернёт значение из side_effect, проигнорировав return_value и обернутый объект целиком. Если установлены любые два значения, значение будет возвращено значением с более высоким приоритетом. Независимо от порядка установки, порядок приоритета остаётся неизменным.
>>> from unittest.mock import Mock >>> class Order: ... @staticmethod ... def get_value(): ... return "third" ... >>> order_mock = Mock(spec=Order, wraps=Order) >>> order_mock.get_value.side_effect = ["first"] >>> order_mock.get_value.return_value = "second" >>> order_mock.get_value() 'first'
Так как None является значением по умолчанию для side_effect, если вы переназначите его обратно на None, порядок приоритета будет проверен между return_value и обернутым объектом, проигнорировав side_effect.
>>> order_mock.get_value.side_effect = None >>> order_mock.get_value() 'second'
Если возвращаемое значение side_effect равно DEFAULT, оно игнорируется, и порядок приоритета переходит к преемнику для получения возвращаемого значения.
>>> from unittest.mock import DEFAULT >>> order_mock.get_value.side_effect = [DEFAULT] >>> order_mock.get_value() 'second'
Когда Mock оборачивает объект, значение по умолчанию для return_value будет DEFAULT.
>>> order_mock = Mock(spec=Order, wraps=Order) >>> order_mock.return_value sentinel.DEFAULT >>> order_mock.get_value.return_value sentinel.DEFAULT
Порядок приоритета проигнорирует это значение и перейдёт к последнему преемнику, которым является обернутый объект.
Поскольку реальный вызов происходит к обернутому объекту, создание экземпляра этого mock вернёт реальный экземпляр класса. Позиционные аргументы, если они есть, необходимые обернутому объекту, должны быть переданы.
>>> order_mock_instance = order_mock() >>> isinstance(order_mock_instance, Order) True >>> order_mock_instance.get_value() 'third'
>>> order_mock.get_value.return_value = DEFAULT >>> order_mock.get_value() 'third'
>>> order_mock.get_value.return_value = "second" >>> order_mock.get_value() 'second'
Но если вы назначите None ему, это не будет проигнорировано, так как это явное назначение. Таким образом, порядок приоритета не перейдёт к обернутому объекту.
>>> order_mock.get_value.return_value = None >>> order_mock.get_value() is None True
Даже если вы зададите все три значения одновременно при инициализации mock, порядок приоритета остаётся тем же:
>>> order_mock = Mock(spec=Order, wraps=Order,
... **{"get_value.side_effect": ["first"],
... "get_value.return_value": "second"}
... )
...
>>> order_mock.get_value()
'first'
>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'
Если side_effect исчерпан, порядок приоритета не вызовет получения значения из преемников. Вместо этого возникает исключение StopIteration.
>>> order_mock = Mock(spec=Order, wraps=Order) >>> order_mock.get_value.side_effect = ["first side effect value", ... "another side effect value"] >>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value() 'first side effect value' >>> order_mock.get_value() 'another side effect value'
>>> order_mock.get_value() Traceback (most recent call last): ... StopIteration
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/unittest.mock.html