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. Это позволяет моделям проходить тесты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 есть имя, оно будет использовано в представлении 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, то только атрибуты в спецификации могут быть установлены.
-
attach_mock(mock, attribute) -
Прикрепление mock в качестве атрибута к этому, заменяя его имя и родителя. Вызовы прикрепленного mock будут записаны в атрибуты
method_callsиmock_callsэтого.
-
configure_mock(**kwargs) -
Установка атрибутов 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То же самое можно сделать в вызове конструктора mocks:
>>> 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)полезными результатами. Для mock с spec это включает все разрешённые атрибуты для mock.См.
FILTER_DIRдля того, что делает эта фильтрация и как её отключить.
-
_get_child_mock(**kw) -
Создание дочерних mock для атрибутов и возвращаемого значения. По умолчанию дочерние mocks будут того же типа, что и родитель. Подклассы Mock могут захотеть переопределить это, чтобы настроить способ создания дочерних mocks.
Для невызываемых mock используется вызываемый вариант (а не любой пользовательский подкласс).
-
called -
Булево значение, представляющее, был ли вызван объект mock:
>>> 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) -
Заглушка, предназначенная для использования в качестве свойства или другого дескриптора в классе.
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) по крайней мере один раз. Обратите внимание, что это отделено от того, был ли вызван объект, ключевое слово
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() -
Проверить, что заглушка была ожидания (await) ровно один раз.
>>> 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) -
Проверить, что последнее ожидание (await) было с указанными аргументами.
>>> 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) -
Проверить, что заглушка была ожидания (await) ровно один раз и с указанными аргументами.
>>> 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) -
Проверить, что заглушка когда-либо была ожидания (await) с указанными аргументами.
>>> 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) с указанными вызовами. Список
await_args_listпроверяется на наличие ожиданий (await).Если any_order ложно, то ожидания (await) должны быть последовательными. До или после указанных ожиданий (await) могут быть дополнительные вызовы.
Если any_order истинно, то ожидания (await) могут быть в любом порядке, но они все должны появиться в
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() -
Проверить, что заглушка никогда не была ожидания (await).
>>> mock = AsyncMock() >>> mock.assert_not_awaited()
-
reset_mock(*args, **kwargs) -
См.
Mock.reset_mock(). Также устанавливаетawait_countв 0,await_argsв None и очищаетawait_args_list.
-
await_count -
Целое число, отслеживающее, сколько раз объект-заглушка был ожидания (await).
>>> mock = AsyncMock() >>> async def main(): ... await mock() ... >>> asyncio.run(main()) >>> mock.await_count 1 >>> asyncio.run(main()) >>> mock.await_count 2
-
await_args -
Это либо
None(если заглушка не была ожидания (await)), либо аргументы, с которыми заглушка была в последний раз ожидания (await). Работает так же, как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 -
Это список всех ожиданий (await), сделанных объекту-заглушке в последовательности (длина списка — количество ожиданий (await)). До того, как ожидания (await) были сделаны, это пустой список.
>>> 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, если они вызываются с неверной сигнатурой. Для mocks, заменяющих класс, их возвращаемое значение (’instance’) будет иметь ту же спецификацию, что и класс. См. функцию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 истинно, то словарь будет очищен перед установкой новых значений.
patch.dict()также может быть вызван с произвольными ключевыми аргументами для установки значений в словарь.Изменено в версии 3.8:
patch.dict()теперь возвращает изменённый словарь при использовании как менеджер контекста.
patch.dict() может использоваться как менеджер контекста, декоратор или декоратор класса:
>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
... assert foo == {'newkey': 'newvalue'}
>>> test()
>>> assert foo == {}
При использовании в качестве декоратора класса patch.dict() учитывает patch.TEST_PREFIX (по умолчанию 'test') для выбора методов для обертывания:
>>> import os
>>> import unittest
>>> from unittest.mock import patch
>>> @patch.dict('os.environ', {'newkey': 'newvalue'})
... class TestSample(unittest.TestCase):
... def test_sample(self):
... self.assertEqual(os.environ['newkey'], 'newvalue')
Если вы хотите использовать другой префикс для своего теста, вы можете сообщить об этом патчерам, установив patch.TEST_PREFIX. Подробнее о том, как изменить значение, см. TEST_PREFIX.
patch.dict() можно использовать для добавления элементов в словарь или просто для изменения словаря в ходе теста, гарантируя, что словарь восстановится по завершении теста.
>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
... assert foo == {'newkey': 'newvalue'}
... assert patched_foo == {'newkey': 'newvalue'}
... # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
... patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
... print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ
Ключевые слова могут быть использованы в вызове patch.dict() для установки значений в словарь:
>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
... import mymodule
... mymodule.function('some', 'args')
...
'fish'
patch.dict() может использоваться с объектами, похожими на словарь, которые на самом деле не являются словарями. Как минимум они должны поддерживать получение, установку, удаление элементов и либо итерацию, либо проверку на принадлежность. Это соответствует магическим методам __getitem__(), __setitem__(), __delitem__() и либо __iter__(), либо __contains__().
>>> class Container:
... def __init__(self):
... self.values = {}
... def __getitem__(self, name):
... return self.values[name]
... def __setitem__(self, name, value):
... self.values[name] = value
... def __delitem__(self, name):
... del self.values[name]
... def __iter__(self):
... return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
... assert thing['one'] == 2
... assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']
patch.multiple
-
patch.multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs) -
Выполняет несколько замещений в одном вызове. Принимает объект для изменения (либо как объект, либо строку для поиска объекта с помощью импорта) и ключевые аргументы для замещений:
with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): ...Используйте
DEFAULTв качестве значения, если вы хотите, чтобыpatch.multiple()создавал заглушки для вас. В этом случае созданные заглушки передаются в декорированную функцию по ключевым словам, а словарь возвращается, когдаpatch.multiple()используется как менеджер контекста.patch.multiple()может использоваться как декоратор, декоратор класса или как менеджер контекста. Аргументы spec, spec_set, create, autospec и new_callable имеют то же значение, что и дляpatch(). Эти аргументы будут применены ко всем замещениям, выполненнымpatch.multiple().При использовании в качестве декоратора класса
patch.multiple()учитываетpatch.TEST_PREFIXдля выбора методов для обертывания.
Если вы хотите, чтобы patch.multiple() создавал заглушки для вас, вы можете использовать DEFAULT в качестве значения. Если вы используете patch.multiple() как декоратор, то созданные заглушки передаются в декорированную функцию по ключевым словам.
>>> thing = object()
>>> other = object()
>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
... assert isinstance(thing, MagicMock)
... assert isinstance(other, MagicMock)
...
>>> test_function()
patch.multiple() может быть вложен с другими patch декораторами, но аргументы, передаваемые по ключевым словам, должны быть после любых стандартных аргументов, созданных patch():
>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
... assert 'other' in repr(other)
... assert 'thing' in repr(thing)
... assert 'exit' in repr(mock_exit)
...
>>> test_function()
Если patch.multiple() используется как менеджер контекста, возвращаемое значение менеджера контекста — это словарь, где созданные заглушки имеют ключ по имени:
>>> with patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) as values:
... assert 'other' in repr(values['other'])
... assert 'thing' in repr(values['thing'])
... assert values['thing'] is thing
... assert values['other'] is other
...
методы patch: start и stop
Все патчеры имеют методы start() и stop(). Это упрощает выполнение патчинга в методах setUp или там, где нужно выполнить несколько патчей без вложенных декораторов или операторов with.
Для их использования вызовите patch(), patch.object() или patch.dict() как обычно и сохраните ссылку на возвращённый patcher объект. Затем вы можете вызвать start() для применения патча и stop() для его отмены.
Если вы используете patch() для создания заглушки для вас, она будет возвращена в результате вызова patcher.start.
>>> patcher = patch('package.module.ClassName')
>>> from package import module
>>> original = module.ClassName
>>> new_mock = patcher.start()
>>> assert module.ClassName is not original
>>> assert module.ClassName is new_mock
>>> patcher.stop()
>>> assert module.ClassName is original
>>> assert module.ClassName is not new_mock
Типичный пример использования - для выполнения нескольких патчей в методе setUp класса TestCase:
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... self.patcher1 = patch('package.module.Class1')
... self.patcher2 = patch('package.module.Class2')
... self.MockClass1 = self.patcher1.start()
... self.MockClass2 = self.patcher2.start()
...
... def tearDown(self):
... self.patcher1.stop()
... self.patcher2.stop()
...
... def test_something(self):
... assert package.module.Class1 is self.MockClass1
... assert package.module.Class2 is self.MockClass2
...
>>> MyTest('test_something').run()
Внимание
Если вы используете этот метод, вы должны убедиться, что патч «отменён» вызовом stop. Это может быть сложнее, чем вы думаете, потому что если в методе setUp произойдёт исключение, tearDown не будет вызван. unittest.TestCase.addCleanup() упрощает это:
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... patcher = patch('package.module.Class')
... self.MockClass = patcher.start()
... self.addCleanup(patcher.stop)
...
... def test_something(self):
... assert package.module.Class is self.MockClass
...
В качестве дополнительного преимущества вам больше не нужно хранить ссылку на patcher объект.
Также возможно остановить все запущенные патчи с помощью patch.stopall().
-
patch.stopall() -
Остановить все активные патчи. Останавливает только патчи, запущенные с помощью
start.
patch builtins
Вы можете заменить встроенные функции в модуле. Следующий пример заменяет встроенную функцию ord():
>>> @patch('__main__.ord')
... def test(mock_ord):
... mock_ord.return_value = 101
... print(ord('c'))
...
>>> test()
101
TEST_PREFIX
Все патчеры могут использоваться в качестве декораторов классов. При использовании таким образом они оборачивают каждый тестовый метод в классе. Патчеры распознают методы, начинающиеся с 'test' как тестовые методы. Это тот же способ, что и у unittest.TestLoader по умолчанию для поиска тестовых методов.
Возможно, вам нужно использовать другой префикс для ваших тестов. Вы можете сообщить патчерам об использовании другого префикса, установив patch.TEST_PREFIX:
>>> patch.TEST_PREFIX = 'foo'
>>> value = 3
>>>
>>> @patch('__main__.value', 'not three')
... class Thing:
... def foo_one(self):
... print(value)
... def foo_two(self):
... print(value)
...
>>>
>>> Thing().foo_one()
not three
>>> Thing().foo_two()
not three
>>> value
3
Вложенные декораторы патчинга
Если вам нужно выполнить несколько патчей, вы можете просто указать их последовательно.
Вы можете объединить несколько декораторов патчинга, используя этот шаблон:
>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
... assert SomeClass.static_method is mock1
... assert SomeClass.class_method is mock2
... SomeClass.static_method('foo')
... SomeClass.class_method('bar')
... return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')
Обратите внимание, что декораторы применяются снизу вверх. Это стандартный способ применения декораторов в Python. Порядок созданных mocks, передаваемых в вашу тестовую функцию, соответствует этому порядку.
Где производить патчинг
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__,__div__,__truediv__,__floordiv__,__mod__,__divmod__,__lshift__,__rshift__,__and__,__xor__,__or__, и__pow__ - Методы преобразования числовых типов:
__complex__,__int__,__float__и__index__ - Методы дескриптора:
__get__,__set__и__delete__ - Запись в pickle:
__reduce__,__reduce_ex__,__getinitargs__,__getnewargs__,__getstate__и__setstate__ - Представление пути файловой системы:
__fspath__ - Методы асинхронной итерации:
__aiter__и__anext__
Изменено в версии 3.8: Добавлена поддержка os.PathLike.__fspath__().
Изменено в версии 3.8: Добавлена поддержка __aenter__, __aexit__, __aiter__ и __anext__.
Следующие методы существуют, но не поддерживаются, так как они используются mock, не могут быть динамически установлены или могут вызывать проблемы:
-
__getattr__,__setattr__,__init__и__new__ -
__prepare__,__instancecheck__,__subclasscheck__,__del__
Магическая модель
Существует два MagicMock варианта: MagicMock и NonCallableMagicMock.
-
class unittest.mock.MagicMock(*args, **kw) -
MagicMock— это подклассMockс реализациями по умолчанию большинства магических методов. Вы можете использоватьMagicMockбез необходимости самостоятельной настройки магических методов.Параметры конструктора имеют то же значение, что и для
Mock.Если вы используете аргументы spec или spec_set, то создадутся только магические методы, присутствующие в спецификации.
-
class unittest.mock.NonCallableMagicMock(*args, **kw) -
Не вызываемый вариант
MagicMock.Параметры конструктора имеют то же значение, что и для
MagicMock, за исключением return_value и side_effect, которые не имеют значения для невызываемой модели.
Магические методы настраиваются с помощью объектов MagicMock, поэтому вы можете их настроить и использовать обычным способом:
>>> mock = MagicMock() >>> mock[3] = 'fish' >>> mock.__setitem__.assert_called_with(3, 'fish') >>> mock.__getitem__.return_value = 'result' >>> mock[2] 'result'
По умолчанию многие методы протокола должны возвращать объекты определенного типа. Эти методы предварительно настроены с возвращаемым значением по умолчанию, чтобы их можно было использовать, не делая ничего, если вам не нужно возвращаемое значение. Вы все равно можете установить возвращаемое значение вручную, если хотите изменить значение по умолчанию.
Методы и их значения по умолчанию:
-
__lt__:NotImplemented -
__gt__:NotImplemented -
__le__:NotImplemented -
__ge__:NotImplemented -
__int__:1 -
__contains__:False -
__len__:0 -
__iter__:iter([]) -
__exit__:False -
__aexit__:False -
__complex__:1j -
__float__:1.0 -
__bool__:True -
__index__:1 -
__hash__: хэш по умолчанию для модели -
__str__: строка по умолчанию для модели -
__sizeof__: размер по умолчанию для модели
Например:
>>> mock = MagicMock() >>> int(mock) 1 >>> len(mock) 0 >>> list(mock) [] >>> object() in mock False
Два метода равенства, __eq__() и __ne__(), являются специальными. Они выполняют сравнение по идентичности по умолчанию, используя атрибут side_effect, если вы не измените их возвращаемое значение на что-то другое:
>>> MagicMock() == 3 False >>> MagicMock() != 3 True >>> mock = MagicMock() >>> mock.__eq__.return_value = True >>> mock == 3 True
Возвращаемое значение MagicMock.__iter__() может быть любым итерируемым объектом и не обязательно итератором:
>>> mock = MagicMock() >>> mock.__iter__.return_value = ['a', 'b', 'c'] >>> list(mock) ['a', 'b', 'c'] >>> list(mock) ['a', 'b', 'c']
Если возвращаемое значение является итератором, то итерация по нему один раз его израсходует, и последующие итерации вернут пустой список:
>>> mock.__iter__.return_value = iter(['a', 'b', 'c']) >>> list(mock) ['a', 'b', 'c'] >>> list(mock) []
MagicMock имеет все поддерживаемые магические методы, настроенные, за исключением некоторых устаревших и малоизвестных. Вы все равно можете настроить их по желанию.
Магические методы, которые поддерживаются, но по умолчанию не настроены в MagicMock, это:
__subclasses____dir____format__-
__get__,__set__и__delete__ -
__reversed__и__missing__ -
__reduce__,__reduce_ex__,__getinitargs__,__getnewargs__,__getstate__и__setstate__ -
__getformat__и__setformat__
-
2 -
Магические методы должны ищятся в классе, а не в экземпляре. Разные версии Python несогласованно применяют это правило. Поддерживаемые методы протокола должны работать со всеми поддерживаемыми версиями Python.
-
3 -
Функция в основном подключена к классу, но каждый
Mockэкземпляр изолирован от остальных.
Справочные средства
sentinel
-
unittest.mock.sentinel -
Объект
sentinelпредоставляет удобный способ предоставления уникальных объектов для ваших тестов.Атрибуты создаются по запросу, когда вы обращаетесь к ним по имени. Обращение к одному и тому же атрибуту всегда возвращает один и тот же объект. Возвращаемые объекты имеют разумное представление, чтобы сообщения об ошибках тестов были удобочитаемыми.
Иногда при тестировании вам нужно проверить, что определённый объект передаётся в качестве аргумента другому методу или возвращается. Часто для этого создаются именованные объекты-маяки. 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, попытка установить атрибуты, которые не существуют в объекте спецификации, вызоветAttributeError.Если в качестве спецификации используется класс, то возвращаемое значение макета (экземпляр класса) будет иметь ту же спецификацию. Вы можете использовать класс как спецификацию для экземпляра объекта, передавая
instance=True. Возвращаемый макет будет вызываемым только в том случае, если экземпляры макета вызываемы.create_autospec()также принимает произвольные ключевые аргументы, которые передаются в конструктор созданного макета.
См. Автоспецификация для примеров использования автоспецификации с create_autospec() и аргументом autospec к patch().
Изменено в версии 3.8: create_autospec() теперь возвращает AsyncMock, если целевой объект — асинхронная функция.
ANY
-
unittest.mock.ANY
Иногда вам может потребоваться сделать утверждения о некоторых аргументах в вызове макета, но либо не заботиться о некоторых аргументах, либо извлечь их индивидуально из call_args и сделать более сложные утверждения о них.
Чтобы проигнорировать определённые аргументы, вы можете передать объекты, которые сравниваются со всем. Вызовы assert_called_with() и assert_called_once_with() затем завершатся успешно независимо от того, что было передано.
>>> mock = Mock(return_value=None)
>>> mock('foo', bar=object())
>>> mock.assert_called_once_with('foo', bar=ANY)
ANY также может использоваться в сравнениях с списками вызовов, такими как mock_calls:
>>> m = MagicMock(return_value=None) >>> m(1) >>> m(1, 2) >>> m(object()) >>> m.mock_calls == [call(1), call(1, 2), ANY] True
FILTER_DIR
-
unittest.mock.FILTER_DIR
FILTER_DIR — это переменная уровня модуля, которая контролирует, как объекты макета реагируют на dir() (только для Python 2.6 и более поздних версий). Значение по умолчанию — True, которое использует фильтрацию, описанную ниже, для отображения только полезных членов. Если вам не нравится эта фильтрация или вам нужно отключить её для диагностических целей, установите mock.FILTER_DIR = False.
При включённой фильтрации dir(some_mock) отображает только полезные атрибуты и включает любые динамически созданные атрибуты, которые обычно не отображаются. Если макет был создан со 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 перематывается в начало. Если вам нужен больший контроль над данными, которые вы передаёте в тестируемый код, вам нужно будет настроить этот мок самостоятельно. В случаях, когда этого недостаточно, пакеты in-memory файловых систем на PyPI могут предложить реалистичную файловую систему для тестирования.Изменено в версии 3.4: Добавлена поддержка
readline()иreadlines(). Мокread()был изменён на потребление read_data вместо возвращения его при каждом вызове.Изменено в версии 3.5: read_data теперь сбрасывается при каждом вызове mock.
Изменено в версии 3.8: В реализацию добавлена поддержка итераций (например, в циклах for), чтобы корректно потреблять read_data.
Использование open() в качестве менеджера контекста — отличный способ обеспечить правильное закрытие файловых потоков, и это становится распространённым способом:
with open('/some/path', 'w') as f:
f.write('something')
Проблема в том, что даже если вы смоделируете вызов open(), именно возвращённый объект используется как менеджер контекста (и в нём вызываются __enter__() и __exit__()).
Моделирование менеджеров контекста с помощью MagicMock достаточно распространено и достаточно сложно, поэтому вспомогательная функция полезна.
>>> m = mock_open()
>>> with patch('__main__.open', m):
... with open('foo', 'w') as h:
... h.write('some stuff')
...
>>> m.mock_calls
[call('foo', 'w'),
call().__enter__(),
call().write('some stuff'),
call().__exit__(None, None, None)]
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')
И для чтения файлов:
>>> with patch('__main__.open', mock_open(read_data='bibble')) as m:
... with open('foo') as h:
... result = h.read()
...
>>> m.assert_called_once_with('foo')
>>> assert result == 'bibble'
Автоспецификация
Автоспецификация основана на существующей spec функции модуля mock. Она ограничивает API моков API исходного объекта (спецификации), но является рекурсивной (реализуется лениво), поэтому атрибуты моков имеют тот же API, что и атрибуты спецификации. Кроме того, смоделированные функции/методы имеют такую же сигнатуру вызова, что и оригинальные, поэтому они генерируют TypeError, если вызываются неправильно.
Прежде чем объяснить, как работает автоспецификация, вот почему она нужна.
Mock — это очень мощный и гибкий объект, но он страдает двумя недостатками при использовании для имитации объектов из системы, подвергаемой тестированию. Один из этих недостатков специфичен для API Mock, а другой — более общая проблема при использовании объектов-моков.
Сначала проблема, специфичная для Mock. Mock имеет два метода assert, которые очень удобны: assert_called_with() и assert_called_once_with().
>>> mock = Mock(name='Thing', return_value=None) >>> mock(1, 2, 3) >>> mock.assert_called_once_with(1, 2, 3) >>> mock(1, 2, 3) >>> mock.assert_called_once_with(1, 2, 3) Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times.
Поскольку моки автоматически создают атрибуты по требованию и позволяют вызывать их с произвольными аргументами, если вы неправильно напишите один из этих методов assert, то ваше утверждение исчезнет:
>>> mock = Mock(name='Thing', return_value=None) >>> mock(1, 2, 3) >>> mock.assret_called_once_with(4, 5, 6) # 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() для создания моков с автоспецификацией напрямую:
>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>
Однако это не лишено недостатков и ограничений, поэтому это не поведение по умолчанию. Для того, чтобы знать, какие атрибуты доступны в объекте спецификации, автоспецификация должна интроспективно исследовать (получить доступ к атрибутам) спецификации. При переходе по атрибутам мока происходит соответствующий переход по исходному объекту в скрытом виде. Если у ваших объектов-спецификаций есть свойства или дескрипторы, которые могут вызвать выполнение кода, то вы можете не иметь возможности использовать автоспецификацию. С другой стороны, намного лучше проектировать свои объекты таким образом, чтобы интроспекция была безопасной 4.
Более серьезная проблема заключается в том, что часто атрибуты экземпляров создаются в методе __init__() и вообще не существуют в классе. autospec не может знать о динамически созданных атрибутах и ограничивает API видимыми атрибутами.
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Существует несколько способов решения этой проблемы. Самый простой, но не обязательно наименее раздражающий способ — просто установить необходимые атрибуты в моке после его создания. Просто потому, что autospec не позволяет получить доступ к атрибутам, которые не существуют в спецификации, это не препятствует их установке:
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a = 33
...
Существует более агрессивная версия как spec, так и autospec, которая препятствует установке несуществующих атрибутов. Это полезно, если вы хотите убедиться, что ваш код устанавливает только допустимые атрибуты, но, очевидно, это предотвращает эту конкретную ситуацию:
>>> with patch('__main__.Something', autospec=True, spec_set=True):
... thing = Something()
... thing.a = 33
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Вероятно, лучший способ решения проблемы — добавить атрибуты класса в качестве значений по умолчанию для членов экземпляра, инициализированных в __init__(). Обратите внимание, что если вы устанавливаете только атрибуты по умолчанию в __init__(), то предоставление их через атрибуты класса (разделяемые, разумеется, между экземплярами) также быстрее. например:
class Something:
a = 33
Это поднимает еще одну проблему. Довольно часто предоставляется значение по умолчанию None для членов, которые впоследствии станут объектом другого типа. None бесполезен в качестве спецификации, потому что он не позволит вам получить доступ к каким-либо атрибутам или методам. Поскольку None никогда не будет полезен в качестве спецификации и, вероятно, указывает на член, который обычно будет другого типа, autospec не использует спецификацию для членов, которые установлены в None. Они будут просто обычными моками (ну — MagicMocks):
>>> class Something: ... member = None ... >>> mock = create_autospec(Something) >>> mock.member.foo.bar.baz() <MagicMock name='mock.member.foo.bar.baz()' id='...'>
Если изменение ваших производственных классов для добавления значений по умолчанию вам не подходит, есть больше вариантов. Один из них — просто использовать экземпляр в качестве спецификации вместо класса. Другой — создать подкласс производственного класса и добавить значения по умолчанию в подкласс без влияния на производственный класс. В обоих случаях вам необходимо использовать альтернативный объект в качестве спецификации. К счастью, patch() поддерживает это — вы можете просто передать альтернативный объект в качестве аргумента autospec:
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> class SomethingForTest(Something):
... a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>
-
4 -
Это относится только к классам или уже созданным объектам. Вызов смоделированного класса для создания экземпляра мока не создает реальный экземпляр. Только поиск атрибутов — наряду с вызовами
dir()— выполняются.
Запечатывание моков
-
unittest.mock.seal(mock) -
Запечатывание отключит автоматическое создание моков при обращении к атрибуту запечатываемого мока или любому его атрибуту, который уже является моком рекурсивно.
Если экземпляр мока с именем или спецификацией присваивается атрибуту, он не будет рассматриваться в цепочке запечатывания. Это позволяет предотвратить исправление части объекта мока запечатыванием.
>>> mock = Mock() >>> mock.submock.attribute1 = 2 >>> mock.not_submock = mock.Mock(name="sample_name") >>> seal(mock) >>> mock.new_attribute # This will raise AttributeError. >>> mock.submock.attribute2 # This will raise AttributeError. >>> mock.not_submock.attribute2 # This won't raise.
Новое в версии 3.7.
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/library/unittest.mock.html