unittest.mock — библиотека объектов-моков
Новая в версии 3.3.
Исходный код: Lib/unittest/mock.py
unittest.mock — это библиотека для тестирования на Python. Она позволяет заменять части вашей системы, подлежащей тестированию, на объекты-моки и делать утверждения о том, как они были использованы.
unittest.mock предоставляет базовый класс Mock, избавляя от необходимости создавать множество заглушек в вашем наборе тестов. После выполнения действия вы можете сделать утверждения о том, какие методы/атрибуты были использованы и с какими аргументами они были вызваны. Вы также можете указать возвращаемые значения и установить необходимые атрибуты обычным способом.
Кроме того, mock предоставляет декоратор patch(), который обрабатывает замену атрибутов модуля и класса в рамках теста, а также sentinel для создания уникальных объектов. Обратитесь к краткому руководству для примеров использования Mock, MagicMock и patch().
Mock предназначен для использования с unittest и основан на паттерне «действие -> утверждение», а не на паттерне «запись -> воспроизведение», используемом многими фреймворками для создания моков.
Существует бэпорт unittest.mock для более ранних версий Python, доступный как mock на PyPI.
Быстрое руководство
Mock и MagicMock объекты создают все атрибуты и методы по мере их доступа и хранят подробности о том, как они были использованы. Вы можете настроить их, чтобы указать возвращаемые значения или ограничить доступные атрибуты, а затем сделать утверждения о том, как они были использованы:
>>> from unittest.mock import MagicMock >>> thing = ProductionClass() >>> thing.method = MagicMock(return_value=3) >>> thing.method(3, 4, 5, key='value') 3 >>> thing.method.assert_called_with(3, 4, 5, key='value')
side_effect позволяет вам выполнять побочные эффекты, включая вызов исключения при вызове мока:
>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()
Traceback (most recent call last):
...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect(arg):
... return values[arg]
...
>>> mock.side_effect = side_effect
>>> mock('a'), mock('b'), mock('c')
(1, 2, 3)
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)
Mock имеет много других способов настройки и управления его поведением. Например, аргумент spec настраивает мок, чтобы он брал свое описание из другого объекта. Попытка получить доступ к атрибутам или методам мока, которые не существуют в описании, приведет к ошибке AttributeError.
Декоратор/менеджер контекста patch() упрощает создание моков классов или объектов в тестируемом модуле. Указанный объект будет заменен моком (или другим объектом) во время теста и восстановлен по завершении теста:
>>> from unittest.mock import patch
>>> @patch('module.ClassName2')
... @patch('module.ClassName1')
... def test(MockClass1, MockClass2):
... module.ClassName1()
... module.ClassName2()
... assert MockClass1 is module.ClassName1
... assert MockClass2 is module.ClassName2
... assert MockClass1.called
... assert MockClass2.called
...
>>> test()
Примечание
Когда вы вкладываете декораторы patch, моки передаются в декорированную функцию в том же порядке, в котором они были применены (обычный порядок Python для применения декораторов). Это означает сверху вниз, поэтому в приведенном примере мок для module.ClassName1 передается первым.
С patch() важно, чтобы вы подменяли объекты в том пространстве имен, где они ищутся. Обычно это просто, но для быстрого руководства прочтите где подменять.
Помимо декоратора, patch() может использоваться как менеджер контекста в операторе with:
>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method: ... thing = ProductionClass() ... thing.method(1, 2, 3) ... >>> mock_method.assert_called_once_with(1, 2, 3)
Также есть patch.dict() для установки значений в словаре только в определенном пространстве и восстановления словаря в исходном состоянии по завершении теста:
>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
... assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original
Mock поддерживает создание моков для магических методов Python магических методов. Самый простой способ использования магических методов — с классом MagicMock. Он позволяет делать такие вещи, как:
>>> mock = MagicMock() >>> mock.__str__.return_value = 'foobarbaz' >>> str(mock) 'foobarbaz' >>> mock.__str__.assert_called_with()
Mock позволяет назначать функции (или другие экземпляры Mock) магическим методам, и они будут вызываться должным образом. Класс MagicMock — просто разновидность Mock, у которой все магические методы созданы заранее (ну, все полезные).
Следующий пример использования магических методов с обычным классом Mock:
>>> mock = Mock() >>> mock.__str__ = Mock(return_value='wheeeeee') >>> str(mock) 'wheeeeee'
Для обеспечения того, чтобы объекты-моки в ваших тестах имели тот же API, что и заменяемые объекты, вы можете использовать авто-спессинг. Авто-спессинг можно сделать с помощью аргумента autospec к patch или функции create_autospec(). Авто-спессинг создает объекты-моки, которые имеют те же атрибуты и методы, что и заменяемые объекты, и любые функции и методы (включая конструкторы) имеют такую же сигнатуру вызова, что и реальный объект.
Это гарантирует, что ваши моки будут работать так же, как ваш рабочий код, если они будут использованы неправильно:
>>> from unittest.mock import create_autospec
>>> def function(a, b, c):
... pass
...
>>> mock_function = create_autospec(function, return_value='fishy')
>>> mock_function(1, 2, 3)
'fishy'
>>> mock_function.assert_called_once_with(1, 2, 3)
>>> mock_function('wrong arguments')
Traceback (most recent call last):
...
TypeError: <lambda>() takes exactly 3 arguments (1 given)
create_autospec() также можно использовать с классами, где он копирует сигнатуру метода __init__, и с вызываемыми объектами, где он копирует сигнатуру метода __call__.
Класс Mock
Mock — это гибкий объект-модель, предназначенный для замены использования заглушек и тестовых удвоений в вашем коде. Моки вызываемы и создают атрибуты как новые моки при их доступе 1. Доступ к одному и тому же атрибуту всегда возвращает один и тот же мок. Моки записывают, как вы их используете, позволяя вам делать утверждения о том, что ваш код с ними делал.
MagicMock — это подкласс Mock со всеми магическими методами, созданными заранее и готовыми к использованию. Также есть варианты без возможности вызова, которые полезны, когда вы создаете моки для объектов, которые не вызываемы: NonCallableMock и NonCallableMagicMock
Декораторы patch() позволяют легко временно заменить классы в определенном модуле объектом Mock. По умолчанию patch() создаст для вас MagicMock. Вы можете указать альтернативный класс Mock с помощью аргумента new_callable к patch().
-
class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)
-
Создайте новый объект
Mock.Mockпринимает несколько необязательных аргументов, которые задают поведение объекта Mock:-
spec: Это может быть список строк или существующий объект (класс или экземпляр), который служит спецификацией для объекта mock. Если вы передаете объект, то список строк формируется путем вызова dir на объекте (исключая неподдерживаемые магические атрибуты и методы). Обращение к любому атрибуту, не входящему в этот список, вызовет
AttributeError.Если spec является объектом (а не списком строк), то
__class__возвращает класс объекта spec. Это позволяет mocks проходить тестыisinstance(). -
spec_set: Более строгая разновидность spec. Если используется, попытка установки или получения атрибута на mock, который не находится в объекте, переданном как spec_set, вызовет
AttributeError. -
side_effect: Функция, вызываемая всякий раз, когда вызывается Mock. См. атрибут
side_effect. Полезно для повышения исключений или динамического изменения возвращаемых значений. Функция вызывается с теми же аргументами, что и mock, и, если она не возвращаетDEFAULT, возвращаемое значение этой функции используется в качестве возвращаемого значения.В качестве альтернативы, side_effect может быть классом или экземпляром исключения. В этом случае исключение будет поднято при вызове mock.
Если side_effect является итерируемым объектом, каждый вызов mock вернет следующее значение из итерируемого объекта.
side_effect может быть очищен, установив его в
None. -
return_value: Возвращаемое значение при вызове mock. По умолчанию это новый Mock (созданный при первом доступе). См. атрибут
return_value. -
unsafe: По умолчанию доступ к любому атрибуту, имя которого начинается с assert, assret, asert, aseert или assrt, вызовет
AttributeError. Передачаunsafe=Trueпозволит получить доступ к этим атрибутам.Добавлено в версии 3.5.
-
wraps: Элемент для обертывания объекта mock. Если wraps не
Noneто вызов Mock передаст вызов обернутому объекту (возвращая реальный результат). Обращение к атрибуту в mock вернет объект Mock, который обертывает соответствующий атрибут обернутого объекта (попытка доступа к атрибуту, которого не существует, вызоветAttributeError).Если у mock явно задано return_value, то вызовы не передаются обернутому объекту, а возвращается return_value.
- name: Если у mock есть имя, то оно будет использоваться в repr mock. Это может быть полезно для отладки. Имя передается дочерним mock.
Mocks также могут вызываться с произвольными именованными аргументами. Эти аргументы будут использоваться для установки атрибутов на mock после его создания. См. метод
configure_mock()для получения дополнительной информации.-
assert_called() -
Утверждение, что mock был вызван как минимум один раз.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called()
Добавлено в версии 3.6.
-
assert_called_once() -
Утверждение, что mock был вызван ровно один раз.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() Traceback (most recent call last): ... AssertionError: Expected 'method' to have been called once. Called 2 times.
Добавлено в версии 3.6.
-
assert_called_with(*args, **kwargs) -
Этот метод является удобным способом утверждения, что последний вызов был выполнен определенным способом:
>>> mock = Mock() >>> mock.method(1, 2, 3, test='wow') <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_with(1, 2, 3, test='wow')
-
assert_called_once_with(*args, **kwargs) -
Утверждение, что mock был вызван ровно один раз и с указанными аргументами.
>>> mock = Mock(return_value=None) >>> mock('foo', bar='baz') >>> mock.assert_called_once_with('foo', bar='baz') >>> mock('other', bar='values') >>> mock.assert_called_once_with('other', bar='values') Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times.
-
assert_any_call(*args, **kwargs) -
Утверждение, что mock был вызван с указанными аргументами.
Утверждение проходит, если mock был вызван хотя бы один раз, в отличие от
assert_called_with()иassert_called_once_with(), которые проходят только если вызов является последним, и в случаеassert_called_once_with(), он также должен быть единственным вызовом.>>> mock = Mock(return_value=None) >>> mock(1, 2, arg='thing') >>> mock('some', 'thing', 'else') >>> mock.assert_any_call(1, 2, arg='thing')
-
assert_has_calls(calls, any_order=False) -
Утверждение, что mock был вызван с указанными вызовами. Проверяется список
mock_calls.Если any_order равно false, то вызовы должны быть последовательными. Перед или после указанных вызовов могут быть дополнительные вызовы.
Если any_order равно true, то вызовы могут быть в любом порядке, но они должны все появиться в
mock_calls.>>> mock = Mock(return_value=None) >>> mock(1) >>> mock(2) >>> mock(3) >>> mock(4) >>> calls = [call(2), call(3)] >>> mock.assert_has_calls(calls) >>> calls = [call(4), call(2), call(3)] >>> mock.assert_has_calls(calls, any_order=True)
-
assert_not_called() -
Утверждение, что mock не был вызван.
>>> m = Mock() >>> m.hello.assert_not_called() >>> obj = m.hello() >>> m.hello.assert_not_called() Traceback (most recent call last): ... AssertionError: Expected 'hello' to not have been called. Called 1 times.
Добавлено в версии 3.5.
-
reset_mock(*, return_value=False, side_effect=False) -
Метод reset_mock сбрасывает все атрибуты вызовов на объекте mock:
>>> mock = Mock(return_value=None) >>> mock('hello') >>> mock.called True >>> mock.reset_mock() >>> mock.called FalseИзменено в версии 3.6: Добавлены два аргумента только с ключевыми словами в функцию reset_mock.
Это может быть полезно, когда вы хотите выполнить серию утверждений, которые повторно используют один и тот же объект. Обратите внимание, что
reset_mock()не очищает возвращаемое значение,side_effectили любые атрибуты дочерних объектов, которые вы установили с помощью обычной присваивания по умолчанию. Если вы хотите сбросить return_value илиside_effect, передайте соответствующий параметр какTrue. Дочерние mocks и mock возвращаемого значения (если таковой имеется) также сбрасываются.Примечание
return_value и
side_effectявляются аргументами только с ключевыми словами.
-
mock_add_spec(spec, spec_set=False) -
Добавить спецификацию к mock. spec может быть объектом или списком строк. Только атрибуты в spec могут быть извлечены как атрибуты из mock.
Если spec_set равно true, то только атрибуты в spec могут быть установлены.
-
attach_mock(mock, attribute) -
Прикрепить mock в качестве атрибута к этому, заменив его имя и родителя. Вызовы прикрепленного mock будут записаны в атрибутах
method_callsиmock_callsэтого.
-
configure_mock(**kwargs) -
Установить атрибуты на mock с помощью именованных аргументов.
Атрибуты, а также возвращаемые значения и эффекты могут быть установлены на дочерних mock с использованием стандартной нотации точек и распаковки словаря в вызове метода:
>>> mock = Mock() >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock.configure_mock(**attrs) >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyErrorТо же самое можно сделать в вызове конструктора mock:
>>> 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()существует, чтобы упростить конфигурацию после создания mock.
-
__dir__() -
Объекты
Mockограничивают результатыdir(some_mock)полезными результатами. Для mocks со spec это включает все разрешенные атрибуты для mock.См.
FILTER_DIR, чтобы узнать, что делает эта фильтрация и как ее отключить.
-
_get_child_mock(**kw) -
Создать дочерние mock для атрибутов и возвращаемого значения. По умолчанию дочерние mock будут того же типа, что и родительский. Подклассы Mock могут переопределить это, чтобы настроить способ создания дочерних mock.
Для невызываемых mocks будет использоваться вызываемая разновидность (а не любой пользовательский подкласс).
-
-
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()
-
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 вызывает исключение, вызов все равно записывается.
Самый простой способ заставить мок вызвать исключение при вызове - сделать side_effect классом или экземпляром исключения:
>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]
Если side_effect является функцией, то то, что возвращает эта функция, и будет возвращено при вызове мока. Функция side_effect вызывается с теми же аргументами, что и мок. Это позволяет динамически изменять значение возврата вызова в зависимости от входных данных:
>>> def side_effect(value): ... return value + 1 ... >>> m = MagicMock(side_effect=side_effect) >>> m(1) 2 >>> m(2) 3 >>> m.mock_calls [call(1), call(2)]
Если вы хотите, чтобы мок по-прежнему возвращал значение по умолчанию (новый мок) или любое заданное значение возврата, есть два способа сделать это. Либо верните mock.return_value изнутри side_effect, либо верните DEFAULT:
>>> m = MagicMock() >>> def side_effect(*args, **kwargs): ... return m.return_value ... >>> m.side_effect = side_effect >>> m.return_value = 3 >>> m() 3 >>> def side_effect(*args, **kwargs): ... return DEFAULT ... >>> m.side_effect = side_effect >>> m() 3
Чтобы удалить side_effect, и вернуться к поведению по умолчанию, установите side_effect в значение None:
>>> m = MagicMock(return_value=6) >>> def side_effect(*args, **kwargs): ... return 3 ... >>> m.side_effect = side_effect >>> m() 3 >>> m.side_effect = None >>> m() 6
side_effect также может быть любым итерируемым объектом. Повторные вызовы к моку будут возвращать значения из итерируемого объекта (пока итерируемый объект не исчерпан и не будет вызвано исключение StopIteration):
>>> m = MagicMock(side_effect=[1, 2, 3]) >>> m() 1 >>> m() 2 >>> m() 3 >>> m() Traceback (most recent call last): ... StopIteration
Если какие-либо члены итерируемого объекта являются исключениями, они будут подняты вместо возвращения:
>>> iterable = (33, ValueError, 66) >>> m = MagicMock(side_effect=iterable) >>> m() 33 >>> m() Traceback (most recent call last): ... ValueError >>> m() 66
Удаление атрибутов
Объекты Mock создают атрибуты по требованию. Это позволяет им имитировать объекты любого типа.
Вам может понадобиться, чтобы объект Mock возвращал False при вызове hasattr() или поднимал исключение AttributeError при получении атрибута. Вы можете сделать это, предоставив объект в качестве spec для мока, но это не всегда удобно.
Вы «блокируете» атрибуты, удаляя их. После удаления доступ к атрибуту приведет к исключению AttributeError.
>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
...
AttributeError: f
Имена Mock и атрибут name
Поскольку «имя» является аргументом конструктора Mock, если вы хотите, чтобы у вашего объекта Mock был атрибут «имя», вы не можете просто передать его во время создания. Есть два варианта. Один вариант - использовать configure_mock():
>>> mock = MagicMock() >>> mock.configure_mock(name='my_name') >>> mock.name 'my_name'
Более простой вариант - просто установить атрибут «имя» после создания мока:
>>> mock = MagicMock() >>> mock.name = "foo"
Присоединение Mock в качестве атрибутов
Когда вы присоединяете мок в качестве атрибута другого мока (или в качестве возвращаемого значения), он становится «дочерним» элементом этого мока. Вызовы к дочернему элементу записываются в атрибутах method_calls и mock_calls родительского элемента. Это полезно для настройки дочерних моков и их присоединения к родительскому элементу или для присоединения моков к родительскому элементу, который записывает все вызовы к дочерним элементам и позволяет проводить утверждения о порядке вызовов между моками:
>>> parent = MagicMock() >>> child1 = MagicMock(return_value=None) >>> child2 = MagicMock(return_value=None) >>> parent.child1 = child1 >>> parent.child2 = child2 >>> child1(1) >>> child2(2) >>> parent.mock_calls [call.child1(1), call.child2(2)]
Исключением является случай, если у мока есть имя. Это позволяет предотвратить «родительские отношения», если по какой-то причине вы этого не хотите.
>>> mock = MagicMock() >>> not_a_child = MagicMock(name='not-a-child') >>> mock.attribute = not_a_child >>> mock.attribute() <MagicMock name='not-a-child()' id='...'> >>> mock.mock_calls []
Моки, созданные для вас функцией patch(), автоматически получают имена. Чтобы присоединить моки, имеющие имена, к родительскому элементу, используйте метод attach_mock():
>>> thing1 = object()
>>> thing2 = object()
>>> parent = MagicMock()
>>> with patch('__main__.thing1', return_value=None) as child1:
... with patch('__main__.thing2', return_value=None) as child2:
... parent.attach_mock(child1, 'child1')
... parent.attach_mock(child2, 'child2')
... child1('one')
... child2('two')
...
>>> parent.mock_calls
[call.child1('one'), call.child2('two')]
-
1 -
Исключениями являются магические методы и атрибуты (те, у которых есть ведущие и заключительные двойные подчеркивания). Mock не создает их, а вместо этого вызывает исключение
AttributeError. Это связано с тем, что интерпретатор часто неявно запрашивает эти методы и сильно «путается», получая новый объект Mock, когда ожидает магический метод. Если вам нужна поддержка магических методов, обратитесь к магическим методам.
Декораторы замены
Декораторы замены используются для замены объектов только в пределах области функции, которую они декорируют. Они автоматически выполняют отмену замены, даже если возникают исключения. Все эти функции также могут быть использованы в операторе with или как декораторы классов.
patch
Примечание
Ключевым моментом является выполнение замены в правильном пространстве имён. См. раздел где выполнить замену.
-
unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs) -
patch()выступает в роли декоратора функций, декоратора классов или менеджера контекста. Внутри тела функции или оператора with, target заменяется объектом new. При выходе из функции/оператора with замена отменяется.Если new опущен, то target заменяется на
AsyncMock, если заменяемый объект является асинхронной функцией, или наMagicMockв противном случае. Еслиpatch()используется как декоратор, а new опущен, созданный mock передаётся в качестве дополнительного аргумента декорируемой функции. Еслиpatch()используется как менеджер контекста, созданный mock возвращается менеджером контекста.target должен быть строкой в формате
'package.module.ClassName'. target импортируется, и указанный объект заменяется объектом new, поэтому target должен быть импортируемым из среды, из которой вы вызываетеpatch(). target импортируется при выполнении декорированной функции, а не во время декорирования.Ключевые аргументы spec и spec_set передаются в
MagicMock, если patch создаёт его для вас.Кроме того, вы можете передать
spec=Trueилиspec_set=True, что заставляет patch передавать объект, который имитируется, как объект spec/spec_set.new_callable позволяет указать другой класс или вызываемый объект, который будет вызван для создания объекта new. По умолчанию используется
AsyncMockдля асинхронных функций иMagicMockдля остальных.Более мощной формой spec является autospec. Если вы установите
autospec=True, то mock будет создан со спецификацией из объекта, который заменяется. Все атрибуты mock также будут иметь спецификацию соответствующего атрибута заменяемого объекта. Методы и функции, которые имитируются, будут проверять свои аргументы и выдаватьTypeError, если они вызываются с неправильной сигнатурой. Для mock, заменяющих класс, их возвращаемое значение («экземпляр») будет иметь такую же спецификацию, как и класс. См. функцию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) на заглушку (mock).
patch.object()может использоваться в качестве декоратора, декоратора класса или менеджера контекста. Аргументы new, spec, create, spec_set, autospec и new_callable имеют такое же значение, как и дляpatch(). Как иpatch(),patch.object()принимает произвольные именованные аргументы для настройки создаваемого объекта-заглушки.При использовании в качестве декоратора класса,
patch.object()учитываетpatch.TEST_PREFIXдля выбора методов, которые нужно обернуть.
Вы можете вызвать patch.object() с тремя аргументами или двумя аргументами. Форма с тремя аргументами принимает объект, который нужно заменить, имя атрибута и объект, который заменит атрибут.
При вызове с двумя аргументами вы опускаете заменяющий объект, и для вас создается заглушка, которая передается в качестве дополнительного аргумента в декорированную функцию:
>>> @patch.object(SomeClass, 'class_method') ... def test(mock_method): ... SomeClass.class_method(3) ... mock_method.assert_called_with(3) ... >>> test()
spec, create и другие аргументы patch.object() имеют такое же значение, как и для patch().
patch.dict
-
patch.dict(in_dict, values=(), clear=False, **kwargs) -
Заменяет словарь (или объект, похожий на словарь), и восстанавливает его исходное состояние после теста.
in_dict может быть словарем или контейнером, подобным словарю. Если это отображение, то оно должно, по крайней мере, поддерживать получение, установку и удаление элементов, а также итерацию по ключам.
in_dict также может быть строкой, указывающей имя словаря, которое затем будет получено с помощью импорта.
values может быть словарем значений, которые нужно установить в словаре. values также может быть итерируемым объектом пар
(key, value).Если clear имеет значение true, то словарь будет очищен перед установкой новых значений.
patch.dict()также может быть вызван с произвольными именованными аргументами для установки значений в словарь.Изменено в версии 3.8:
patch.dict()теперь возвращает заменённый словарь при использовании в качестве менеджера контекста.
patch.dict() может использоваться в качестве менеджера контекста, декоратора или декоратора класса:
>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
... assert foo == {'newkey': 'newvalue'}
>>> test()
>>> assert foo == {}
При использовании в качестве декоратора класса, patch.dict() учитывает patch.TEST_PREFIX (по умолчанию 'test') для выбора методов, которые нужно обернуть:
>>> import os
>>> import unittest
>>> from unittest.mock import patch
>>> @patch.dict('os.environ', {'newkey': 'newvalue'})
... class TestSample(unittest.TestCase):
... def test_sample(self):
... self.assertEqual(os.environ['newkey'], 'newvalue')
Если вы хотите использовать другой префикс для своих тестов, вы можете сообщить об этом патчеру, установив patch.TEST_PREFIX. Для получения дополнительных сведений о том, как изменить значение, см. TEST_PREFIX.
patch.dict() может использоваться для добавления элементов в словарь или для изменения словаря в тесте, а также гарантирует восстановление словаря по завершении теста.
>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
... assert foo == {'newkey': 'newvalue'}
... assert patched_foo == {'newkey': 'newvalue'}
... # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
... patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
... print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ
Именованные аргументы могут использоваться в вызове patch.dict() для установки значений в словарь:
>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
... import mymodule
... mymodule.function('some', 'args')
...
'fish'
patch.dict() может использоваться с объектами, подобными словарям, которые на самом деле не являются словарями. По крайней мере, они должны поддерживать получение, установку, удаление элементов и либо итерацию, либо проверку на принадлежность. Это соответствует магическим методам __getitem__(), __setitem__(), __delitem__() и либо __iter__(), либо __contains__().
>>> class Container:
... def __init__(self):
... self.values = {}
... def __getitem__(self, name):
... return self.values[name]
... def __setitem__(self, name, value):
... self.values[name] = value
... def __delitem__(self, name):
... del self.values[name]
... def __iter__(self):
... return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
... assert thing['one'] == 2
... assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']
patch.multiple
-
patch.multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs) -
Выполняет несколько замещений в одном вызове. Она принимает объект, который нужно заменить (либо как объект, либо строку для получения объекта посредством импорта), и именованные аргументы для замещений:
with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): ...Используйте
DEFAULTв качестве значения, если вы хотите, чтобыpatch.multiple()создавала заглушки за вас. В этом случае созданные заглушки передаются в декорированную функцию по имени, а словарь возвращается, когдаpatch.multiple()используется как менеджер контекста.patch.multiple()может использоваться в качестве декоратора, декоратора класса или менеджера контекста. Аргументы spec, spec_set, create, autospec и new_callable имеют такое же значение, как и дляpatch(). Эти аргументы будут применяться ко всем замещениям, выполняемымpatch.multiple().При использовании в качестве декоратора класса
patch.multiple()учитываетpatch.TEST_PREFIXдля выбора методов, которые нужно обернуть.
Если вы хотите, чтобы patch.multiple() создавала заглушки за вас, то вы можете использовать DEFAULT в качестве значения. Если вы используете patch.multiple() как декоратор, то созданные заглушки передаются в декорированную функцию по имени.
>>> thing = object()
>>> other = object()
>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
... assert isinstance(thing, MagicMock)
... assert isinstance(other, MagicMock)
...
>>> test_function()
patch.multiple() может быть вложен с другими patch декораторами, но аргументы, передаваемые по имени, должны стоять после любых стандартных аргументов, созданных patch():
>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
... assert 'other' in repr(other)
... assert 'thing' in repr(thing)
... assert 'exit' in repr(mock_exit)
...
>>> test_function()
Если patch.multiple() используется как менеджер контекста, возвращаемое значение менеджера контекста — это словарь, в котором созданные заглушки имеют ключи по имени:
>>> with patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) as values:
... assert 'other' in repr(values['other'])
... assert 'thing' in repr(values['thing'])
... assert values['thing'] is thing
... assert values['other'] is other
...
методы патчера: start и stop
Все патчеры имеют методы start() и stop(). Они упрощают выполнение замещений в методах setUp или там, где требуется выполнить несколько замещений без вложенных декораторов или инструкций with.
Чтобы использовать их, вызовите patch(), patch.object() или patch.dict() как обычно и сохраните ссылку на возвращаемый patcher объект. Затем вы можете вызвать start() для размещения замещения и stop() для его отмены.
Если вы используете patch() для создания заглушки, то она будет возвращена в результате вызова patcher.start.
>>> patcher = patch('package.module.ClassName')
>>> from package import module
>>> original = module.ClassName
>>> new_mock = patcher.start()
>>> assert module.ClassName is not original
>>> assert module.ClassName is new_mock
>>> patcher.stop()
>>> assert module.ClassName is original
>>> assert module.ClassName is not new_mock
Типичный пример использования этого подхода — выполнение нескольких замещений в методе setUp класса TestCase:
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... self.patcher1 = patch('package.module.Class1')
... self.patcher2 = patch('package.module.Class2')
... self.MockClass1 = self.patcher1.start()
... self.MockClass2 = self.patcher2.start()
...
... def tearDown(self):
... self.patcher1.stop()
... self.patcher2.stop()
...
... def test_something(self):
... assert package.module.Class1 is self.MockClass1
... assert package.module.Class2 is self.MockClass2
...
>>> MyTest('test_something').run()
Внимание
Если вы используете этот метод, вы должны убедиться, что замещение "отменяется" вызовом stop. Это может оказаться сложнее, чем ожидалось, поскольку если в методе setUp произойдет исключение, то tearDown не будет вызван. unittest.TestCase.addCleanup() упрощает этот процесс:
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... patcher = patch('package.module.Class')
... self.MockClass = patcher.start()
... self.addCleanup(patcher.stop)
...
... def test_something(self):
... assert package.module.Class is self.MockClass
...
Дополнительным преимуществом является то, что вам больше не нужно сохранять ссылку на patcher объект.
Также возможно остановить все начатые замещения, используя patch.stopall().
-
patch.stopall() -
Останавливает все активные замещения. Останавливает только замещения, запущенные с помощью
start.
Модификация встроенных функций
Вы можете модифицировать любые встроенные функции внутри модуля. Следующий пример модифицирует встроенную функцию ord():
>>> @patch('__main__.ord')
... def test(mock_ord):
... mock_ord.return_value = 101
... print(ord('c'))
...
>>> test()
101
Префикс TEST
Все модификаторы могут использоваться как декораторы классов. При использовании таким образом они оборачивают каждый тестовый метод класса. Модификаторы распознают методы, начинающиеся с '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__.
Следующие методы существуют, но не поддерживаются, так как они либо используются 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__: Значение по умолчанию для str заглушки -
__sizeof__: Значение по умолчанию для sizeof заглушки
Например:
>>> mock = MagicMock() >>> int(mock) 1 >>> len(mock) 0 >>> list(mock) [] >>> object() in mock False
Два метода равенства, __eq__() и __ne__(), являются специальными. Они выполняют сравнение по идентификатору по умолчанию, используя атрибут side_effect, если вы не измените их возвращаемое значение.
>>> MagicMock() == 3 False >>> MagicMock() != 3 True >>> mock = MagicMock() >>> mock.__eq__.return_value = True >>> mock == 3 True
Возвращаемое значение MagicMock.__iter__() может быть любым итерируемым объектом и не обязательно должно быть итератором.
>>> mock = MagicMock() >>> mock.__iter__.return_value = ['a', 'b', 'c'] >>> list(mock) ['a', 'b', 'c'] >>> list(mock) ['a', 'b', 'c']
Если возвращаемое значение является итератором, то однократное итерирование по нему его израсходует, а последующие итерирования приведут к пустому списку.
>>> mock.__iter__.return_value = iter(['a', 'b', 'c']) >>> list(mock) ['a', 'b', 'c'] >>> list(mock) []
MagicMock содержит все поддерживаемые магические методы, за исключением некоторых устаревших и малоиспользуемых.
Магические методы, которые поддерживаются, но не настраиваются по умолчанию в MagicMock:
__subclasses____dir____format__-
__get__,__set__и__delete__ -
__reversed__и__missing__ -
__reduce__,__reduce_ex__,__getinitargs__,__getnewargs__,__getstate__и__setstate__ __getformat__
-
2 -
Магические методы должны быть вызываемы через класс, а не экземпляр. Различные версии Python не согласны с этим правилом. Поддерживаемые методы протокола должны работать со всеми поддерживаемыми версиями Python.
-
3 -
Функция по сути привязана к классу, но каждый экземпляр
Mockизолирован от других.
Справочные функции
sentinel
-
unittest.mock.sentinel -
Объект
sentinelпредоставляет удобный способ предоставления уникальных объектов для ваших тестов.Атрибуты создаются по запросу при обращении к ним по имени. Обращение к одному и тому же атрибуту всегда возвращает один и тот же объект. Возвращаемые объекты имеют осмысленный repr, чтобы сообщения об ошибках тестов были удобочитаемыми.
Иногда при тестировании вам нужно проверить, что определенный объект передается в качестве аргумента другому методу или возвращается. Часто для этого создаются именованные объекты-маяки. 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
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)) (члены типа), чтобы обойти фильтрацию независимо от 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 — это очень мощный и гибкий объект, но он страдает двумя недостатками при использовании для подмены объектов в тестируемой системе. Один из этих недостатков специфичен для Mock API, а другой — более общая проблема использования поддельных объектов.
Сначала проблема, специфичная для Mock. У Mock есть два метода assert, которые очень удобны: assert_called_with() и assert_called_once_with().
>>> mock = Mock(name='Thing', return_value=None) >>> mock(1, 2, 3) >>> mock.assert_called_once_with(1, 2, 3) >>> mock(1, 2, 3) >>> mock.assert_called_once_with(1, 2, 3) Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times.
Поскольку поддельные объекты автоматически создают атрибуты по запросу и позволяют вызывать их с произвольными аргументами, если вы неправильно напишете один из этих методов assert, то ваше утверждение исчезнет:
>>> mock = Mock(name='Thing', return_value=None) >>> mock(1, 2, 3) >>> mock.assret_called_once_with(4, 5, 6) # Intentional typo!
Ваши тесты могут пройти молча и неправильно из-за опечатки.
Вторая проблема более общая для подмены. Если вы переименуете часть своего кода, переименуете члены и так далее, любые тесты для кода, который все еще использует старый API, но использует поддельные объекты вместо реальных, все равно пройдут. Это означает, что ваши тесты могут пройти успешно, даже если ваш код сломан.
Обратите внимание, что это еще одна причина, по которой вам нужны интеграционные тесты, а также модульные тесты. Проверка всего в изоляции — это хорошо, но если вы не проверяете, как ваши модули «связаны вместе», все еще есть много места для ошибок, которые могли бы поймать тесты.
mock уже предоставляет функцию для решения этой проблемы, называемую спецификацией. Если вы используете класс или экземпляр в качестве spec для поддельного объекта, то вы можете получить доступ только к атрибутам поддельного объекта, которые существуют в реальном классе:
>>> from urllib import request >>> mock = Mock(spec=request.Request) >>> mock.assret_called_with # Intentional typo! Traceback (most recent call last): ... AttributeError: Mock object has no attribute 'assret_called_with'
Спецификация применяется только к самому поддельному объекту, поэтому у нас все еще есть та же проблема с любыми методами в поддельном объекте:
>>> mock.has_data() <mock.Mock object at 0x...> >>> mock.has_data.assret_called_with() # Intentional typo!
Автоспессинг решает эту проблему. Вы можете передать autospec=True в patch() / patch.object() или использовать функцию create_autospec() для создания поддельного объекта со спецификацией. Если вы используете аргумент autospec=True в patch(), то объект, который заменяется, будет использоваться в качестве объекта спецификации. Поскольку спецификация выполняется «лениво» (спецификация создается при обращении к атрибутам поддельного объекта), вы можете использовать ее с очень сложными или глубоко вложенными объектами (например, модулями, которые импортируют модули, которые импортируют модули), без большой потери производительности.
Вот пример его использования:
>>> from urllib import request
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> request is mock_request
True
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>
Вы можете видеть, что request.Request имеет спецификацию. request.Request принимает два аргумента в конструкторе (один из которых — self). Вот что произойдет, если мы попытаемся вызвать его неправильно:
>>> req = request.Request() Traceback (most recent call last): ... TypeError: <lambda>() takes at least 2 arguments (1 given)
Спецификация также применяется к экземплярам классов (т. е. к возвращаемому значению специфицированных поддельных объектов):
>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>
Request объекты не вызываемы, поэтому возвращаемое значение создания экземпляра нашего замененного request.Request — это невызываемый поддельный объект. При наличии спецификации любые опечатки в наших утверждениях приведут к ошибке:
>>> req.add_header('spam', 'eggs')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_with # Intentional typo!
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')
Во многих случаях вы сможете просто добавить autospec=True к своим существующим вызовам patch(), а затем защититься от ошибок из-за опечаток и изменений API.
Помимо использования autospec через patch(), есть create_autospec() для непосредственного создания поддельных объектов autospecced:
>>> 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. Они будут просто обычными поддельными объектами (ну, MagicMocks):
>>> class Something: ... member = None ... >>> mock = create_autospec(Something) >>> mock.member.foo.bar.baz() <MagicMock name='mock.member.foo.bar.baz()' id='...'>
Если изменение ваших производственных классов для добавления значений по умолчанию не по душе, есть другие варианты. Один из них — использовать экземпляр в качестве спецификации вместо класса. Другой — создать подкласс производственного класса и добавить значения по умолчанию в подкласс без изменения производственного класса. Для обоих вариантов вам нужен другой объект в качестве спецификации. К счастью, patch() поддерживает это — вы можете просто передать альтернативный объект как аргумент autospec:
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> class SomethingForTest(Something):
... a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>
-
4 -
Это относится только к классам или уже существующим объектам. Вызов поддельного класса для создания экземпляра поддельного объекта не создает реальный экземпляр. Это только поиск атрибутов — вместе с вызовами
dir()— что делается.
Запечатывание поддельных объектов
-
unittest.mock.seal(mock) -
Запечатывание отключит автоматическое создание поддельных объектов при обращении к атрибуту запечатанного поддельного объекта или любому из его атрибутов, которые уже являются поддельными объектами рекурсивно.
Если экземпляр поддельного объекта с именем или спецификацией назначается атрибуту, он не будет рассматриваться в цепочке запечатывания. Это позволяет предотвратить исправление части объекта поддельного объекта.
>>> mock = Mock() >>> mock.submock.attribute1 = 2 >>> mock.not_submock = mock.Mock(name="sample_name") >>> seal(mock) >>> mock.new_attribute # This will raise AttributeError. >>> mock.submock.attribute2 # This will raise AttributeError. >>> mock.not_submock.attribute2 # This won't raise.
New in version 3.7.
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/unittest.mock.html