unittest.mock — библиотека объектов-заглушек
Новая в версии 3.3.
Исходный код: Lib/unittest/mock.py
unittest.mock — это библиотека для тестирования на Python. Она позволяет заменить части вашей системы, подлежащей тестированию, объектами-заглушками и делать утверждения о том, как они были использованы.
unittest.mock предоставляет базовый класс Mock, устраняя необходимость создавать множество заглушек в вашем наборе тестов. После выполнения действия вы можете сделать утверждения о том, какие методы/атрибуты были использованы и с какими аргументами они были вызваны. Вы также можете указать значения возврата и установить необходимые атрибуты обычным способом.
Кроме того, mock предоставляет декоратор patch(), который обрабатывает замену атрибутов модуля и класса в рамках теста, а также sentinel для создания уникальных объектов. Обратитесь к краткому руководству для примеров использования Mock, MagicMock и patch().
Mock очень прост в использовании и предназначен для использования с unittest. Mock основан на паттерне «действие -> утверждение» вместо «запись -> воспроизведение», используемом во многих фреймворках для мокирования.
Существует бэктопорт unittest.mock для более ранних версий Python, доступный как mock на PyPI.
1. Краткое руководство
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__.
2. Класс Mock
Mock — гибкий объект-заглушка, призванный заменить использование заглушек и тестовых двойников по всему вашему коду. Заглушки вызываемы и создают атрибуты как новые заглушки при доступе к ним [1]. Обращение к одному и тому же атрибуту всегда возвращает одну и ту же заглушку. Заглушки записывают, как вы их используете, позволяя делать утверждения о том, что ваш код с ними делал.
MagicMock — это подкласс Mock со всеми магическими методами, предварительно созданными и готовыми к использованию. Также существуют невызываемые варианты, полезные при подмене объектов, которые не вызываемы: NonCallableMock и NonCallableMagicMock
Декораторы patch() упрощают временную замену классов в определенном модуле объектом Mock. По умолчанию patch() создаст для вас MagicMock. Вы можете указать альтернативный класс Mock с помощью аргумента new_callable к patch().
-
class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs) -
Создайте новый объект
Mock.Mockпринимает несколько необязательных аргументов, которые определяют поведение объекта Mock:-
spec: Это может быть либо список строк, либо существующий объект (класс или экземпляр), который служит спецификацией для объекта мока. Если вы передаёте объект, то список строк формируется путём вызова dir на объекте (исключая неподдерживаемые магические атрибуты и методы). Доступ к любому атрибуту, не входящему в этот список, вызовет
AttributeError.Если spec является объектом (а не списком строк), то
__class__возвращает класс объекта spec. Это позволяет мокам проходить тестыisinstance(). -
spec_set: Более жёсткая разновидность spec. Если используется, попытка установить или получить атрибут мока, который отсутствует в объекте, переданном в качестве spec_set, вызовет
AttributeError. -
side_effect: Функция, которая вызывается всякий раз, когда вызывается Mock. См. атрибут
side_effect. Полезно для повышения исключений или динамического изменения возвращаемых значений. Функция вызывается с теми же аргументами, что и мок, и, если она не возвращаетDEFAULT, возвращаемое значение этой функции используется как возвращаемое значение.В качестве альтернативы side_effect может быть классом или экземпляром исключения. В этом случае исключение будет поднято при вызове мока.
Если side_effect является итерируемым объектом, то каждый вызов мока вернёт следующее значение из этого итерируемого объекта.
side_effect может быть очищен, установив его в
None. -
return_value: Значение, возвращаемое при вызове мока. По умолчанию это новый Mock (созданный при первом обращении). См. атрибут
return_value. -
unsafe: По умолчанию, если любой атрибут начинается с assert или assret, он вызовет
AttributeError. Передачаunsafe=Trueпозволит получить доступ к этим атрибутам.Новое в версии 3.5.
-
wraps: Элемент для обертывания объекта мока. Если wraps не
None, то вызов Mock передаст вызов обернутому объекту (возвращая реальный результат). Обращение к атрибуту мока вернёт объект Mock, который обернёт соответствующий атрибут обернутого объекта (попытка доступа к атрибуту, которого не существует, вызоветAttributeError).Если для мока явно задано return_value, вызовы не передаются обернутому объекту, и вместо этого возвращается return_value.
- name: Если у мока есть имя, оно будет использоваться в repr мока. Это может быть полезно для отладки. Имя передаётся дочерним мокам.
Моки также могут вызываться с произвольными именованными аргументами. Они будут использованы для установки атрибутов на моке после его создания. См. метод
configure_mock()для получения подробностей.-
assert_called_with(*args, **kwargs) -
Этот метод является удобным способом проверки, что вызовы выполняются определённым образом:
>>> mock = Mock() >>> mock.method(1, 2, 3, test='wow') <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_with(1, 2, 3, test='wow')
-
assert_called_once_with(*args, **kwargs) -
Проверить, что мок был вызван ровно один раз и с указанными аргументами.
>>> mock = Mock(return_value=None) >>> mock('foo', bar='baz') >>> mock.assert_called_once_with('foo', bar='baz') >>> mock('other', bar='values') >>> mock.assert_called_once_with('other', bar='values') Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times.
-
assert_any_call(*args, **kwargs) -
проверить, что мок был вызван с указанными аргументами.
Проверка проходит, если мок был вызван хотя бы один раз, в отличие от
assert_called_with()иassert_called_once_with(), которые проходят только если вызов является последним, и в случаеassert_called_once_with()он также должен быть единственным вызовом.>>> mock = Mock(return_value=None) >>> mock(1, 2, arg='thing') >>> mock('some', 'thing', 'else') >>> mock.assert_any_call(1, 2, arg='thing')
-
assert_has_calls(calls, any_order=False) -
Проверяет, что мок был вызван с указанными вызовами. Проверяется список
mock_calls.Если any_order равно false (по умолчанию), то вызовы должны быть последовательными. До или после указанных вызовов могут быть дополнительные вызовы.
Если any_order равно true, то вызовы могут быть в любом порядке, но они все должны присутствовать в
mock_calls.>>> mock = Mock(return_value=None) >>> mock(1) >>> mock(2) >>> mock(3) >>> mock(4) >>> calls = [call(2), call(3)] >>> mock.assert_has_calls(calls) >>> calls = [call(4), call(2), call(3)] >>> mock.assert_has_calls(calls, any_order=True)
-
assert_not_called() -
Проверить, что мок никогда не вызывался.
>>> m = Mock() >>> m.hello.assert_not_called() >>> obj = m.hello() >>> m.hello.assert_not_called() Traceback (most recent call last): ... AssertionError: Expected 'hello' to not have been called. Called 1 times.
Новое в версии 3.5.
-
reset_mock() -
Метод reset_mock сбрасывает все атрибуты вызовов в объекте мока:
>>> mock = Mock(return_value=None) >>> mock('hello') >>> mock.called True >>> mock.reset_mock() >>> mock.called FalseЭто может быть полезно, когда вам нужно выполнить серию утверждений, повторно использующих один и тот же объект. Обратите внимание, что
reset_mock()не очищает возвращаемое значение,side_effectили любые дочерние атрибуты, которые вы установили с помощью обычного присваивания. Дочерние моки и возвращаемый мок (если таковой имеется) также сбрасываются.
-
mock_add_spec(spec, spec_set=False) -
Добавить спецификацию в мок. spec может быть объектом или списком строк. Только атрибуты в spec могут быть получены как атрибуты из мока.
Если spec_set равно true, то только атрибуты в спецификации могут быть установлены.
-
attach_mock(mock, attribute) -
Прикрепить мок в качестве атрибута к этому, заменив его имя и родителя. Вызовы прикреплённого мока будут записываться в атрибуты
method_callsиmock_callsэтого.
-
configure_mock(**kwargs) -
Установить атрибуты на моке через именованные аргументы.
Атрибуты, а также возвращаемые значения и побочные эффекты могут быть установлены на дочерних моках с использованием стандартной записи с точкой и распаковкой словаря в вызове метода:
>>> mock = Mock() >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock.configure_mock(**attrs) >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyErrorТо же самое можно сделать в вызове конструктора для моков:
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock = Mock(some_attribute='eggs', **attrs) >>> mock.some_attribute 'eggs' >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyErrorconfigure_mock()существует, чтобы упростить конфигурацию после создания мока.
-
__dir__() -
объекты
Mockограничивают результатыdir(some_mock)полезными результатами. Для моков со spec это включает все разрешённые атрибуты для мока.См.
FILTER_DIRдля того, что делает эта фильтрация и как её отключить.
-
_get_child_mock(**kw) -
Создаёт дочерние моки для атрибутов и возвращаемого значения. По умолчанию дочерние моки будут того же типа, что и родительский. Подклассы Mock могут переопределить это, чтобы настроить способ создания дочерних моков.
Для невызываемых моков будет использоваться вызываемый вариант (а не любой пользовательский подкласс).
-
called -
Логическое значение, представляющее, был ли вызван объект мока:
>>> mock = Mock(return_value=None) >>> mock.called False >>> mock() >>> mock.called True
-
call_count -
Целое число, показывающее, сколько раз был вызван объект мока:
>>> mock = Mock(return_value=None) >>> mock.call_count 0 >>> mock() >>> mock() >>> mock.call_count 2
-
return_value -
Установите это для настройки возвращаемого значения при вызове мока:
>>> mock = Mock() >>> mock.return_value = 'fish' >>> mock() 'fish'
Значение по умолчанию для возврата — объект мока, и вы можете настроить его обычным способом:
>>> mock = Mock() >>> mock.return_value.attribute = sentinel.Attribute >>> mock.return_value() <Mock name='mock()()' id='...'> >>> mock.return_value.assert_called_with()
return_valueтакже может быть установлено в конструкторе:>>> mock = Mock(return_value=3) >>> mock.return_value 3 >>> mock() 3
-
-
side_effect -
Это может быть функция, которая будет вызываться при вызове мока, итерируемый объект или исключение (класс или экземпляр), которое нужно поднять.
Если вы передадите функцию, она будет вызвана с теми же аргументами, что и мок, и, если функция не вернёт синглтон
DEFAULT, вызов мока вернёт то, что вернёт функция. Если функция вернётDEFAULT, мок вернёт своё обычное значение (изreturn_value).Если вы передадите итерируемый объект, он будет использован для получения итератора, который должен возвращать значение при каждом вызове. Это значение может быть экземпляром исключения, которое нужно поднять, или значением, которое должно быть возвращено при вызове мока (
DEFAULTобработка аналогична случаю с функцией).Пример мока, который поднимает исключение (для тестирования обработки исключений API):
>>> mock = Mock() >>> mock.side_effect = Exception('Boom!') >>> mock() Traceback (most recent call last): ... Exception: Boom!Использование
side_effectдля возвращения последовательности значений:>>> mock = Mock() >>> mock.side_effect = [3, 2, 1] >>> mock(), mock(), mock() (3, 2, 1)
Использование вызываемого объекта:
>>> mock = Mock(return_value=3) >>> def side_effect(*args, **kwargs): ... return DEFAULT ... >>> mock.side_effect = side_effect >>> mock() 3
side_effectможет быть установлено в конструкторе. Вот пример, который добавляет единицу к значению, с которым вызывается мок, и возвращает его:>>> side_effect = lambda value: value + 1 >>> mock = Mock(side_effect=side_effect) >>> mock(3) 4 >>> mock(-8) -7
Установка
side_effectвNoneочищает его:>>> m = Mock(side_effect=KeyError, return_value=3) >>> m() Traceback (most recent call last): ... KeyError >>> m.side_effect = None >>> m() 3
-
call_args -
Это либо
None(если мок не был вызван), либо аргументы, с которыми мок был в последний раз вызван. Это будет в виде кортежа: первый член — это любые упорядоченные аргументы, с которыми был вызван мок (или пустой кортеж), а второй член — это любые ключевые аргументы (или пустой словарь).>>> 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(3, 4, 5, key='fish', next='w00t!') >>> mock.call_args call(3, 4, 5, key='fish', next='w00t!')
call_args, наряду с членами списковcall_args_list,method_callsиmock_callsявляются объектамиcall. Это кортежи, поэтому их можно распаковать, чтобы получить отдельные аргументы и выполнить более сложные проверки. См. вызовы в виде кортежей.
-
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. Их можно распаковать как кортежи, чтобы получить отдельные аргументы. См. вызовы в виде кортежей.
-
__class__ -
Обычно атрибут
__class__объекта вернёт его тип. Для объекта мока сspec,__class__возвращает класс спецификации вместо этого. Это позволяет объектам мока проходить тестыisinstance()для объекта, который они заменяют/маскируют:>>> mock = Mock(spec=3) >>> isinstance(mock, int) True
__class__можно присвоить, это позволяет моку пройти проверкуisinstance(), не заставляя вас использовать спецификацию:>>> mock = Mock() >>> mock.__class__ = dict >>> isinstance(mock, dict) True
-
-
class unittest.mock.NonCallableMock(spec=None, wraps=None, name=None, spec_set=None, **kwargs) -
Невызываемый вариант
Mock. Параметры конструктора имеют то же значение, что иMock, за исключением return_value и side_effect, которые не имеют смысла для невызываемого мока.
Объекты моков, которые используют класс или экземпляр в качестве spec или spec_set, способны проходить тесты isinstance():
>>> mock = Mock(spec=SomeClass) >>> isinstance(mock, SomeClass) True >>> mock = Mock(spec_set=SomeClass()) >>> isinstance(mock, SomeClass) True
Классы Mock поддерживают создание магических методов. См. магические методы для получения полной информации.
Классы моков и декораторы patch() принимают произвольные ключевые аргументы для конфигурации. Для декораторов patch() ключевые аргументы передаются в конструктор создаваемого мока. Ключевые аргументы предназначены для настройки атрибутов мока:
>>> m = MagicMock(attribute=3, other='fish') >>> m.attribute 3 >>> m.other 'fish'
Возвращаемое значение и побочные эффекты дочерних моков можно задать аналогичным образом, используя точечную нотацию. Поскольку вы не можете использовать точечные имена напрямую в вызове, вам нужно создать словарь и распаковать его, используя **:
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
...
KeyError
Вызываемый мок, который был создан со спецификацией (или спецификацией spec_set), будет инспектировать сигнатуру объекта спецификации при сопоставлении вызовов мока. Поэтому он может соответствовать фактическим аргументам вызова независимо от того, были ли они переданы позиционно или по имени:
>>> def f(a, b, c): pass ... >>> mock = Mock(spec=f) >>> mock(1, 2, c=3) <Mock name='mock()' id='140161580456576'> >>> mock.assert_called_with(1, 2, 3) >>> mock.assert_called_with(a=1, b=2, c=3)
Это относится к assert_called_with(), assert_called_once_with(), assert_has_calls() и assert_any_call(). При автоматическом создании спецификации (autospeccing) это также будет применяться к вызовам методов объекта мока.
Изменено в версии 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()
2.1. Вызов
Объекты-заглушки являются вызываемыми. Вызов вернёт значение, установленное в качестве атрибута return_value. Значение по умолчанию — новый объект Mock; он создаётся в первый раз, когда значение возврата обращается (явно или путём вызова Mock) — но сохраняется и возвращается каждый раз одинаковый.
Вызовы, выполненные с объектом, будут записаны в атрибутах, таких как call_args и call_args_list.
Если атрибут side_effect установлен, то он будет вызван после того, как вызов был записан, поэтому если side_effect вызывает исключение, вызов всё равно записывается.
Самый простой способ заставить заглушку вызвать исключение при вызове — установить side_effect как класс исключения или его экземпляр:
>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]
Если side_effect является функцией, то всё, что возвращает эта функция, возвращается вызовами заглушки. Функция side_effect вызывается с теми же аргументами, что и заглушка. Это позволяет динамически изменять возвращаемое значение вызова в зависимости от входных данных:
>>> def side_effect(value): ... return value + 1 ... >>> m = MagicMock(side_effect=side_effect) >>> m(1) 2 >>> m(2) 3 >>> m.mock_calls [call(1), call(2)]
Если вы хотите, чтобы заглушка всё ещё возвращала значение по умолчанию (новый объект Mock) или любое заданное значение возврата, есть два способа. Либо вернуть mock.return_value изнутри side_effect, либо вернуть DEFAULT:
>>> m = MagicMock() >>> def side_effect(*args, **kwargs): ... return m.return_value ... >>> m.side_effect = side_effect >>> m.return_value = 3 >>> m() 3 >>> def side_effect(*args, **kwargs): ... return DEFAULT ... >>> m.side_effect = side_effect >>> m() 3
Чтобы удалить side_effect, и вернуться к стандартному поведению, установите side_effect в None:
>>> m = MagicMock(return_value=6) >>> def side_effect(*args, **kwargs): ... return 3 ... >>> m.side_effect = side_effect >>> m() 3 >>> m.side_effect = None >>> m() 6
side_effect также может быть любым итерируемым объектом. Повторные вызовы заглушки будут возвращать значения из итерируемого объекта (пока итерируемый объект не исчерпан и не возникнет исключение StopIteration):
>>> m = MagicMock(side_effect=[1, 2, 3]) >>> m() 1 >>> m() 2 >>> m() 3 >>> m() Traceback (most recent call last): ... StopIteration
Если какие-либо члены итерируемого объекта являются исключениями, они будут подняты вместо возвращения:
>>> iterable = (33, ValueError, 66) >>> m = MagicMock(side_effect=iterable) >>> m() 33 >>> m() Traceback (most recent call last): ... ValueError >>> m() 66
2.2. Удаление атрибутов
Объекты-заглушки создают атрибуты по требованию. Это позволяет им имитировать объекты любого типа.
Возможно, вам нужно, чтобы объект-заглушка возвращал 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
2.3. Имена заглушек и атрибут name
Поскольку «name» является аргументом конструктора Mock, если вы хотите, чтобы ваш объект-заглушка имел атрибут «name», вы не можете просто передать его во время создания. Есть два варианта. Один вариант — использовать configure_mock():
>>> mock = MagicMock() >>> mock.configure_mock(name='my_name') >>> mock.name 'my_name'
Более простой вариант — просто установить атрибут «name» после создания заглушки:
>>> mock = MagicMock() >>> mock.name = "foo"
2.4. Присоединение заглушек как атрибутов
Когда вы присоединяете заглушку как атрибут другой заглушке (или как значение возврата), она становится «дочерней» заглушкой. Вызовы дочерней заглушки записываются в атрибутах 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] | Исключением являются магические методы и атрибуты (те, что имеют двойные подчёркивания в начале и конце). Заглушка не создаёт их, а вместо этого поднимает AttributeError. Это связано с тем, что интерпретатор часто неявно запрашивает эти методы, и становится очень сбит с толку, получив новый объект Mock, когда он ожидает магический метод. Если вам нужна поддержка магических методов, см. магические методы. |
3. Декораторы для патчинга
Декораторы патчинга используются для патчинга объектов только в рамках функции, которую они декорируют. Они автоматически выполняют отмену патчинга, даже если возникают исключения. Все эти функции также могут использоваться в операторах with или как декораторы классов.
3.1. patch
Примечание
patch() прост в использовании. Ключ — сделать патчинг в правильном пространстве имён. См. раздел где делать патчинг.
-
unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs) -
patch()действует как декоратор функции, декоратор класса или менеджер контекста. Внутри тела функции или оператора with, target подменяется объектом new. При выходе из функции/оператора with подмена отменяется.Если new опущено, то target заменяется на
MagicMock. Еслиpatch()используется как декоратор и new опущено, созданный mock передаётся в качестве дополнительного аргумента декорируемой функции. Еслиpatch()используется как менеджер контекста, созданный mock возвращается менеджером контекста.target должно быть строкой в формате
'package.module.ClassName'. target импортируется, и указанный объект заменяется объектом new, поэтому target должен быть импортируем из среды, из которой вы вызываетеpatch(). Target импортируется при выполнении декорированной функции, а не во время декорирования.Ключевые аргументы spec и spec_set передаются в
MagicMock, если patch создаёт mock за вас.Кроме того, вы можете передать
spec=Trueилиspec_set=True, что заставляет patch передать объект, который имитируется, в качестве объекта spec/spec_set.new_callable позволяет указать другой класс или вызываемый объект, который будет вызван для создания объекта new. По умолчанию используется
MagicMock.Более мощная форма spec — это autospec. Если вы установите
autospec=True, то mock будет создан со спецификацией из объекта, который заменяется. Все атрибуты mock также будут иметь спецификацию соответствующего атрибута заменяемого объекта. Методы и функции, которые имитируются, будут проверять свои аргументы и вызыватьTypeError, если они вызываются с неправильной сигнатурой. Для mocks, заменяющих класс, их возвращаемое значение («экземпляр») будет иметь ту же спецификацию, что и класс. См. функциюcreate_autospec()и Автоспецификацию.Вместо
autospec=Trueвы можете передатьautospec=some_objectдля использования произвольного объекта в качестве спецификации вместо того, который заменяется.По умолчанию
patch()не будет пытаться заменить атрибуты, которые не существуют. Если вы передадитеcreate=True, а атрибута не существует, patch создаст атрибут для вас при вызове отмонтированной функции и удалит его после этого. Это полезно для написания тестов на атрибуты, которые создаются вашим производственным кодом во время выполнения. Это выключено по умолчанию, потому что может быть опасно. Включив его, вы можете писать проходящие тесты для API, которые фактически не существуют!Примечание
Изменено в версии 3.5: Если вы подменяете встроенные функции в модуле, то вам не нужно передавать
create=True, оно будет добавлено по умолчанию.Patch может использоваться как декоратор класса
TestCase. Он работает путём декорации каждого тестового метода в классе. Это уменьшает объём кода, когда ваши тестовые методы используют общий набор подмен.patch()находит тесты, проверяя имена методов, которые начинаются сpatch.TEST_PREFIX. По умолчанию это'test', что соответствует тому, какunittestнаходит тесты. Вы можете указать альтернативный префикс, установивpatch.TEST_PREFIX.Patch может использоваться как менеджер контекста с оператором with. Здесь подмена применяется к блоку с отступами после оператора with. Если вы используете «as», то отмонтированный объект будет связан с именем после «as»; очень полезно, если
patch()создаёт для вас объект mock.patch()принимает произвольные ключевые аргументы. Они будут переданы вMock(или new_callable) при построении.patch.dict(...),patch.multiple(...)иpatch.object(...)доступны для альтернативных вариантов использования.
patch() как декоратор функции, создавая для вас mock и передавая его в декорированную функцию:
>>> @patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
... print(mock_class is SomeClass)
...
>>> function(None)
True
Подмена класса заменяет класс экземпляром MagicMock. Если класс создаётся в тестируемом коде, то это будет return_value mock, который будет использоваться.
Если класс создаётся несколько раз, вы можете использовать side_effect для возвращения нового mock каждый раз. В качестве альтернативы вы можете установить return_value на любое значение, которое вам нужно.
Для настройки значений возврата методов экземпляров в отмонтированном классе вы должны сделать это для return_value. Например:
>>> class Class:
... def method(self):
... pass
...
>>> with patch('__main__.Class') as MockClass:
... instance = MockClass.return_value
... instance.method.return_value = 'foo'
... assert Class() is instance
... assert Class().method() == 'foo'
...
Если вы используете spec или spec_set и patch() заменяет класс, то значение возврата созданного mock будет иметь ту же спецификацию.
>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()
Аргумент new_callable полезен в тех случаях, когда вы хотите использовать альтернативный класс для создания mock вместо стандартного MagicMock. Например, если вы хотите использовать 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, атрибуты дочерних mocks также могут быть настроены. Они не являются синтаксически допустимыми для непосредственного передачи в качестве ключевых аргументов, но словарь с ними в качестве ключей всё ещё можно расширить в вызов 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
3.2. patch.object
-
patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs) -
Подменяет указанный член (attribute) объекта (target) объектом mock.
patch.object()может использоваться как декоратор, декоратор класса или менеджер контекста. Аргументы new, spec, create, spec_set, autospec и new_callable имеют то же значение, что и дляpatch(). Как иpatch(),patch.object()принимает произвольные ключевые аргументы для настройки создаваемого объекта mock.При использовании в качестве декоратора класса
patch.object()учитываетpatch.TEST_PREFIXдля выбора методов, которые нужно обернуть.
Вы можете вызвать patch.object() с тремя аргументами или двумя. Форма с тремя аргументами принимает объект, который нужно отмонтировать, имя атрибута и объект, которым нужно заменить атрибут.
При вызове с двумя аргументами вы пропускаете объект замены, и для вас создаётся mock, который передаётся в качестве дополнительного аргумента декорированной функции:
>>> @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().
3.3. patch.dict
-
patch.dict(in_dict, values=(), clear=False, **kwargs) -
Заменить словарь или похожий на словарь объект и восстановить исходное состояние словаря после теста.
in_dict может быть словарем или контейнером, похожим на словарь. Если это отображение, то оно должно, по крайней мере, поддерживать получение, установку и удаление элементов, а также итерацию по ключам.
in_dict также может быть строкой, указывающей имя словаря, которое затем будет получено путем его импорта.
values может быть словарем значений для установки в словарь. values также может быть итерируемым объектом пар
(key, value).Если clear имеет значение true, то словарь будет очищен перед установкой новых значений.
patch.dict()также может вызываться с произвольными ключевыми аргументами для установки значений в словарь.patch.dict()может использоваться как менеджер контекста, декоратор или декоратор класса. При использовании в качестве декоратора классаpatch.dict()учитываетpatch.TEST_PREFIXдля выбора методов, которые следует обернуть.
patch.dict() можно использовать для добавления элементов в словарь или просто для того, чтобы тест изменял словарь, и гарантировать, что словарь будет восстановлен по завершении теста.
>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}):
... assert foo == {'newkey': 'newvalue'}
...
>>> assert 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']
3.4. 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
...
3.5. Методы patch: start и stop
Все загрузчики имеют методы start() и stop(). Они упрощают замену в методах setUp или там, где требуется несколько замещений без вложенных декораторов или с инструкциями.
Для их использования вызовите 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(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(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.
3.6. patch builtins
Вы можете заменить любые встроенные функции в модуле. В следующем примере заменяется встроенная функция ord():
>>> @patch('__main__.ord')
... def test(mock_ord):
... mock_ord.return_value = 101
... print(ord('c'))
...
>>> test()
101
3.7. 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
3.8. Вложение декораторов patch
Если вам нужно выполнить несколько замещений, вы можете просто добавить декораторы.
Вы можете добавить несколько декораторов patch, используя этот шаблон:
>>> @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. Порядок переданных в вашу тестовую функцию созданных подделок соответствует этому порядку.
3.9. Где производить замену
patch() работает, временно изменяя объект, на который указывает name, на другой. Может быть много имен, указывающих на любой отдельный объект, поэтому для работы замещения необходимо убедиться, что вы заменяете имя, используемое тестируемой системой.
Основной принцип заключается в том, что вы заменяете то место, где ищется объект, а не то место, где он определен. Несколько примеров помогут прояснить это.
Представьте, что у нас есть проект, который мы хотим протестировать со следующей структурой:
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')
3.10. Подмена дескрипторов и прокси-объектов
Как patch, так и patch.object корректно подменяют и восстанавливают дескрипторы: методы класса, статические методы и свойства. Вы должны подменять их на классе, а не на экземпляре. Они также работают с некоторыми объектами, которые проксируют доступ к атрибутам, например, с объектом настроек django.
4. MagicMock и поддержка магических методов
4.1. Моделирование магических методов
Mock поддерживает моделирование методов протокола Python, также известных как «магические методы». Это позволяет объектам-мокам заменять контейнеры или другие объекты, которые реализуют протоколы Python.
Поскольку магические методы ищутся по-другому, чем обычные методы [2], эта поддержка была реализована специально. Это означает, что поддерживаются только определённые магические методы. Список поддерживаемых методов включает почти все из них. Если вы обнаружите какие-либо отсутствующие, которые вам нужны, сообщите нам об этом.
Вы моделируете магические методы, устанавливая интересующий вас метод в функцию или экземпляр мока. Если вы используете функцию, то она должна принимать self в качестве первого аргумента [3].
>>> def __str__(self): ... return 'fooble' ... >>> mock = Mock() >>> mock.__str__ = __str__ >>> str(mock) 'fooble'
>>> mock = Mock() >>> mock.__str__ = Mock() >>> mock.__str__.return_value = 'fooble' >>> str(mock) 'fooble'
>>> mock = Mock() >>> mock.__iter__ = Mock(return_value=iter([])) >>> list(mock) []
Один из вариантов использования — моделирование объектов, используемых в качестве менеджеров контекста в операторе with:
>>> mock = Mock() >>> mock.__enter__ = Mock(return_value='foo') >>> mock.__exit__ = Mock(return_value=False) >>> with mock as m: ... assert m == 'foo' ... >>> mock.__enter__.assert_called_with() >>> mock.__exit__.assert_called_with(None, None, None)
Вызовы магических методов не отображаются в method_calls, но они записываются в mock_calls.
Примечание
Если вы используете ключевой аргумент spec для создания мока, то попытка установить магический метод, который не указан в spec, вызовет AttributeError.
Полный список поддерживаемых магических методов:
-
__hash__,__sizeof__,__repr__и__str__ -
__dir__,__format__и__subclasses__ -
__floor__,__trunc__и__ceil__ - Сравнения:
__lt__,__gt__,__le__,__ge__,__eq__и__ne__ - Методы контейнеров:
__getitem__,__setitem__,__delitem__,__contains__,__len__,__iter__,__reversed__и__missing__ - Менеджер контекста:
__enter__и__exit__ - Унарные числовые методы:
__neg__,__pos__и__invert__ - Числовые методы (включая варианты правой части и на месте):
__add__,__sub__,__mul__,__matmul__,__div__,__truediv__,__floordiv__,__mod__,__divmod__,__lshift__,__rshift__,__and__,__xor__,__or__, и__pow__ - Методы преобразования чисел:
__complex__,__int__,__float__и__index__ - Методы дескрипторов:
__get__,__set__и__delete__ - Сериализация:
__reduce__,__reduce_ex__,__getinitargs__,__getnewargs__,__getstate__и__setstate__
Следующие методы существуют, но не поддерживаются, так как они либо используются моком, либо не могут быть установлены динамически, либо могут вызвать проблемы:
-
__getattr__,__setattr__,__init__и__new__ -
__prepare__,__instancecheck__,__subclasscheck__,__del__
4.2. Magic Mock
Существует два MagicMock варианта: MagicMock и NonCallableMagicMock.
-
class unittest.mock.MagicMock(*args, **kw) -
MagicMock— подклассMockс реализациями по умолчанию большинства магических методов. Вы можете использоватьMagicMockбез необходимости настраивать магические методы.Параметры конструктора имеют тот же смысл, что и для
Mock.Если вы используете аргументы spec или spec_set, то будут созданы только магические методы, присутствующие в spec.
-
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 -
__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__и__setformat__
| [2] | Магические методы должны иследоваться по классу, а не по экземпляру. Различные версии Python не согласны в применении этого правила. Поддерживаемые методы протокола должны работать со всеми поддерживаемыми версиями Python. |
| [3] | Функция, по сути, подключается к классу, но каждый Mock экземпляр сохраняется изолированно от других. |
5. Помощники
5.1. sentinel
-
unittest.mock.sentinel -
Объект
sentinelпредоставляет удобный способ предоставления уникальных объектов для ваших тестов.Атрибуты создаются по требованию при обращении к ним по имени. Обращение к одному и тому же атрибуту всегда возвращает один и тот же объект. Возвращаемые объекты имеют осмысленное представление, чтобы сообщения об ошибках тестов были читаемыми.
Атрибуты
sentinelне сохраняют свою идентичность приcopiedилиpickled.
Иногда при тестировании нужно проверить, что определенный объект передается в качестве аргумента другому методу или возвращается. Для проверки этого часто создаются именованные объекты-сентинэли. sentinel предоставляет удобный способ создания и проверки идентичности таких объектов.
В этом примере мы применяем monkey patching к method для возвращения sentinel.some_object:
>>> real = ProductionClass() >>> real.method = Mock(name="method") >>> real.method.return_value = sentinel.some_object >>> result = real.method() >>> assert result is sentinel.some_object >>> sentinel.some_object sentinel.some_object
5.2. DEFAULT
-
unittest.mock.DEFAULT -
Объект
DEFAULT— это предварительно созданный сентинэль (на самом делеsentinel.DEFAULT). Он может использоваться функциямиside_effectдля указания на то, что следует использовать обычное возвращаемое значение.
5.3. 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
>>> args, kwargs = kall
>>> args
(1, 2, 3)
>>> kwargs
{'arg2': 'two', 'arg': 'one'}
>>> args is kall[0]
True
>>> 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
{'arg2': 'three', 'arg': 'two'}
>>> name is m.mock_calls[0][0]
True
5.4. 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().
5.5. 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
5.6. 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_once_with', 'assert_called_with', 'assert_has_calls', '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.
5.7. 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.
Использование 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'
5.8. Автоспецификация
Автоспецификация основана на существующей spec функции mock. Она ограничивает API подстановок API исходного объекта (спецификации), но является рекурсивной (реализованной лениво), так что атрибуты подстановок имеют тот же API, что и атрибуты спецификации. Кроме того, подставленные функции/методы имеют тот же сигнатурный вызов, что и оригиналы, поэтому они генерируют TypeError, если вызваны неправильно.
Прежде чем объяснить, как работает автоспецификация, вот почему она нужна.
Mock — очень мощный и гибкий объект, но у него есть два недостатка при использовании для подстановки объектов из тестируемой системы. Один из этих недостатков специфичен для API Mock, а другой — более общая проблема использования объектов подстановки.
Сначала проблема, специфичная для Mock. Mock имеет два метода проверки, которые очень удобны: 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.
Поскольку подстановки автоматически создают атрибуты по запросу и позволяют вызывать их с произвольными аргументами, если вы неправильно напишете один из этих методов проверки, ваша проверка исчезнет:
>>> mock = Mock(name='Thing', return_value=None) >>> mock(1, 2, 3) >>> mock.assret_called_once_with(4, 5, 6)
Ваши тесты могут пройти молча и неправильно из-за опечатки.
Вторая проблема более общая для подстановок. Если вы перефакторили часть своего кода, переименовали члены и так далее, любые тесты для кода, который всё ещё использует старый API, но использует подстановки вместо реальных объектов, всё равно пройдут. Это означает, что ваши тесты могут пройти, даже если ваш код сломан.
Обратите внимание, что это ещё одна причина, по которой вам нужны интеграционные тесты наряду с модульными тестами. Тестирование всего в изоляции вполне хорошо, но если вы не тестируете, как ваши модули «связаны», всё ещё есть место для ошибок, которые тесты могли бы обнаружить.
mock уже предоставляет функцию для помощи в этом, называемую спецификацией. Если вы используете класс или экземпляр в качестве spec для подстановки, то вы можете получить доступ только к атрибутам подстановки, которые существуют в реальном классе:
>>> from urllib import request >>> mock = Mock(spec=request.Request) >>> mock.assret_called_with Traceback (most recent call last): ... AttributeError: Mock object has no attribute 'assret_called_with'
Спецификация применяется только к самой подстановке, поэтому у нас по-прежнему есть та же проблема с любыми методами в подстановке:
>>> mock.has_data() <mock.Mock object at 0x...> >>> mock.has_data.assret_called_with()
Автоспецификация решает эту проблему. Вы можете либо передать autospec=True в patch() / patch.object(), или использовать функцию create_autospec() для создания подстановки со спецификацией. Если вы используете аргумент autospec=True в patch(), то объект, который заменяется, будет использоваться как объект спецификации. Поскольку спецификация выполняется «лениво» (спецификация создаётся при обращении к атрибутам подстановки), вы можете использовать её с очень сложными или глубоко вложенными объектами (например, модулями, которые импортируют модули, которые импортируют модули), без большой потери производительности.
Вот пример её использования:
>>> from urllib import request
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> request is mock_request
True
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>
Вы можете видеть, что request.Request имеет спецификацию. request.Request принимает два аргумента в конструкторе (один из которых — self). Вот что происходит, если мы попытаемся вызвать его неправильно:
>>> req = request.Request() Traceback (most recent call last): ... TypeError: <lambda>() takes at least 2 arguments (1 given)
Спецификация также применяется к экземплярам классов (т.е. возвращаемому значению специфицированных подстановок):
>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>
Объекты Request не вызываемы, поэтому возвращаемое значение создания нашего подставленного request.Request — это не вызываемая подстановка. При наличии спецификации любые опечатки в наших проверках будут вызывать соответствующую ошибку:
>>> req.add_header('spam', 'eggs')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_with
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')
Во многих случаях вы можете просто добавить autospec=True к своим существующим вызовам patch(), а затем будете защищены от ошибок, связанных с опечатками и изменениями API.
Помимо использования autospec через patch(), есть create_autospec() для непосредственного создания автоспецифицированных подстановок:
>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>
Однако, это не без оговорок и ограничений, вот почему это не поведение по умолчанию. Чтобы узнать, какие атрибуты доступны в объекте спецификации, автоспецификация должна интроспектировать (обращаться к атрибутам) спецификацию. При переходе по атрибутам подстановки происходит соответствующий переход по исходному объекту в скрытом режиме. Если у каких-либо ваших специфицированных объектов есть свойства или дескрипторы, которые могут вызвать выполнение кода, то вы не сможете использовать автоспецификацию. С другой стороны, гораздо лучше разработать свои объекты так, чтобы интроспекция была безопасной [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() — что выполняется. |
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.5/library/unittest.mock.html