unittest.mock
— библиотека насмешливых объектов¶
Добавлено в версии 3.3.
Исходный код: Lib/unittest/mock.py.
unittest.mock
- это библиотека для тестирования в Python. Она позволяет заменять части тестируемой системы имитационными объектами и делать утверждения о том, как они были использованы.
unittest.mock
предоставляет основной класс Mock
, избавляющий от необходимости создавать множество заглушек в вашем тестовом наборе. После выполнения действия вы можете сделать утверждения о том, какие методы / атрибуты были использованы и с какими аргументами они были вызваны. Вы также можете указать возвращаемые значения и установить необходимые атрибуты обычным способом.
Кроме того, mock предоставляет декоратор patch()
, который обрабатывает изменение атрибутов уровня модуля и класса в пределах области действия теста, а также sentinel
для создания уникальных объектов. Примеры использования quick guide, Mock
и MagicMock
смотрите в patch()
.
Mock разработан для использования с unittest
и основан на модели «действие -> утверждение» вместо «запись -> воспроизведение», используемой многими мокинг-фреймворками.
Существует бэкпорт unittest.mock
для более ранних версий Python, доступный как mock on 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()
Примечание
При вложении декораторов патчей имитаторы передаются в декорированную функцию в том же порядке, в котором они применялись (обычный Python порядок применения декораторов). Это значит снизу вверх, поэтому в приведенном выше примере сначала передается имитатор для module.ClassName1
.
При использовании patch()
важно, чтобы вы сопрягали объекты в том пространстве имен, в котором они ищутся. Обычно это просто, но для краткого руководства прочтите where to 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 magic methods. Самый простой способ использования магических методов - это класс 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, что и объекты, которые они заменяют, вы можете использовать auto-speccing. Автоспецификация может быть выполнена с помощью аргумента autospec в патче или функции 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: Это может быть либо список строк, либо существующий объект (класс или экземпляр), который выступает в качестве спецификации для объекта-макета. Если вы передаете объект, то список строк формируется путем вызова dir на объекте (исключая неподдерживаемые магические атрибуты и методы). Доступ к любому атрибуту, не входящему в этот список, вызовет ошибку
AttributeError
.Если spec является объектом (а не списком строк), то
__class__
возвращает класс объекта spec. Это позволяет мокам проходить тестыisinstance()
.spec_set: Более строгий вариант spec. При его использовании попытка set или get атрибута на имитаторе, который не находится на объекте, переданном в качестве spec_set, вызовет ошибку
AttributeError
.side_effect: Функция, которая будет вызываться каждый раз, когда вызывается Mock. См. атрибут
side_effect
. Полезно для создания исключений или динамического изменения возвращаемых значений. Функция вызывается с теми же аргументами, что и mock, и если она не возвращаетDEFAULT
, в качестве возвращаемого значения используется значение этой функции.В качестве альтернативы side_effect может быть классом или экземпляром исключения. В этом случае исключение будет вызвано при вызове имитатора.
Если side_effect является итерируемым, то каждый вызов mock будет возвращать следующее значение из итерируемого.
Эффект side_effect можно убрать, установив его в значение
None
.возвращаемое_значение: Значение, возвращаемое при вызове макета. По умолчанию это новый Mock (создается при первом обращении). См. атрибут
return_value
.unsafe: По умолчанию доступ к любому атрибуту, имя которого начинается с assert, assret, asert, aseert или assrt, вызовет ошибку
AttributeError
. Передачаunsafe=True
разрешит доступ к этим атрибутам.Добавлено в версии 3.5.
обертки: Элемент для обертывания объекта имитатора. Если wraps не
None
, то вызов Mock передаст вызов обернутому объекту (возвращая реальный результат). Обращение к атрибутам объекта mock вернет объект Mock, который обертывает соответствующий атрибут обернутого объекта (поэтому попытка обращения к несуществующему атрибуту вызовет ошибкуAttributeError
).Если у имитатора явно задано обратное_значение, то вызовы не передаются обернутому объекту, а вместо него возвращается обратное_значение.
name: Если у имитатора есть имя, то оно будет использоваться в репрезентации имитатора. Это может быть полезно для отладки. Имя передается дочерним имитаторам.
Макеты также могут быть вызваны с произвольными аргументами в виде ключевых слов. Они будут использоваться для установки атрибутов макета после его создания. Подробнее см. метод
configure_mock()
.-
assert_called
()¶ Утверждение, что подражатель был вызван хотя бы один раз.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called()
Добавлено в версии 3.6.
-
assert_called_once
()¶ Утверждение, что подражатель был вызван ровно один раз.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() Traceback (most recent call last): ... AssertionError: Expected 'method' to have been called once. Called 2 times.
Добавлено в версии 3.6.
-
assert_called_with
(*args, **kwargs)¶ Этот метод - удобный способ утверждения, что последний вызов был выполнен определенным образом:
>>> mock = Mock() >>> mock.method(1, 2, 3, test='wow') <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_with(1, 2, 3, test='wow')
-
assert_called_once_with
(*args, **kwargs)¶ Утверждает, что имитатор был вызван ровно один раз, и этот вызов был с указанными аргументами.
>>> mock = Mock(return_value=None) >>> mock('foo', bar='baz') >>> mock.assert_called_once_with('foo', bar='baz') >>> mock('other', bar='values') >>> mock.assert_called_once_with('other', bar='values') Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times.
-
assert_any_call
(*args, **kwargs)¶ подтвердить, что имитатор был вызван с указанными аргументами.
Утверждение проходит, если подражатель всегда вызывался, в отличие от
assert_called_with()
иassert_called_once_with()
, которые проходят только если вызов самый последний, а в случаеassert_called_once_with()
он также должен быть единственным.>>> mock = Mock(return_value=None) >>> mock(1, 2, arg='thing') >>> mock('some', 'thing', 'else') >>> mock.assert_any_call(1, 2, arg='thing')
-
assert_has_calls
(calls, any_order=False)¶ Убедиться, что имитатор был вызван с указанными вызовами. Список
mock_calls
проверяется на наличие вызовов.Если any_order равно 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
(*, return_value=False, side_effect=False)¶ Метод reset_mock сбрасывает все атрибуты вызова для объекта-макета:
>>> mock = Mock(return_value=None) >>> mock('hello') >>> mock.called True >>> mock.reset_mock() >>> mock.called False
Изменено в версии 3.6: В функцию reset_mock добавлен аргумент «только два ключевых слова».
Это может быть полезно, когда вы хотите сделать серию утверждений, в которых повторно используется один и тот же объект. Обратите внимание, что
reset_mock()
не очищает возвращаемое значение,side_effect
или любые дочерние атрибуты, которые вы установили с помощью обычного присваивания по умолчанию. Если вы хотите сбросить обратное_значение илиside_effect
, то передайте соответствующий параметр какTrue
. Дочерние макеты и макет возвращаемого значения (если таковой имеется) также сбрасываются.Примечание
обратное_значение, и
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): ... KeyError
configure_mock()
существует для того, чтобы облегчить настройку после создания макета.
-
__dir__
()¶ Объекты
Mock
ограничивают результатыdir(some_mock)
полезными результатами. Для макетов с spec это включает все разрешенные атрибуты для макета.Что делает эта фильтрация и как ее отключить, смотрите в
FILTER_DIR
.
-
_get_child_mock
(**kw)¶ Создайте дочерние макеты для атрибутов и возвращаемого значения. По умолчанию дочерние модели будут того же типа, что и родительская. Подклассы Mock могут захотеть переопределить этот параметр, чтобы настроить способ создания дочерних имитаторов.
Для не вызываемых макетов будет использоваться вызываемый вариант (а не любой пользовательский подкласс).
-
called
¶ Булево значение, показывающее, был ли вызван объект mock или нет:
>>> mock = Mock(return_value=None) >>> mock.called False >>> mock() >>> mock.called True
-
call_count
¶ Целое число, показывающее, сколько раз был вызван объект mock:
>>> 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() >>> 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
).Если вы передаете iterable, он используется для получения итератора, который должен выдавать значение при каждом вызове. Это значение может быть либо экземпляром исключения, которое должно быть вызвано, либо значением, которое должно быть возвращено из вызова имитатора (обработка
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
может быть задано в конструкторе. Вот пример, который добавляет единицу к значению, с которым вызывается mock, и возвращает ее:>>> 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
, это любые упорядоченные аргументы, с которыми был вызван mock (или пустой кортеж), а второй член, к которому также можно получить доступ через свойство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
. Это кортежи, поэтому их можно распаковать, чтобы получить отдельные аргументы и сделать более сложные утверждения. См. calls as tuples.Изменено в версии 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
. Их можно распаковать как кортежи, чтобы получить отдельные аргументы. См. calls as tuples.
-
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
. Их можно распаковать как кортежи, чтобы получить отдельные аргументы. См. calls as tuples.
-
mock_calls
¶ mock_calls
записывает все вызовы к объекту mock, его методам, магическим методам и возвращаемым значениям mock.>>> 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
. Их можно распаковать как кортежи, чтобы получить отдельные аргументы. См. calls as tuples.Примечание
Способ записи
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, которые не имеют значения в невызываемом mock.
Объекты Mock, которые используют класс или экземпляр в качестве spec
или spec_set
, могут проходить isinstance()
тесты:
>>> mock = Mock(spec=SomeClass)
>>> isinstance(mock, SomeClass)
True
>>> mock = Mock(spec_set=SomeClass())
>>> isinstance(mock, SomeClass)
True
Классы Mock
имеют поддержку насмешки над магическими методами. Подробную информацию смотрите в magic methods.
Классы 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 (или spec_set), будет анализировать сигнатуру объекта спецификации при сопоставлении вызовов имитатора. Таким образом, он может соответствовать аргументам фактического вызова независимо от того, были ли они переданы позиционно или по имени:
>>> def f(a, b, c): pass
...
>>> mock = Mock(spec=f)
>>> mock(1, 2, c=3)
<Mock name='mock()' id='140161580456576'>
>>> mock.assert_called_with(1, 2, 3)
>>> mock.assert_called_with(a=1, b=2, c=3)
Это относится к assert_called_with()
, assert_called_once_with()
, assert_has_calls()
и assert_any_call()
. Когда Автоспецификация, это также будет применяться к вызовам методов на объекте-макете.
Изменено в версии 3.4: Добавлена интроспекция сигнатур для специфицированных и автоспецифицированных mock-объектов.
-
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
не определено, функция async вернет значение, определенноеreturn_value
, следовательно, по умолчанию функция async возвращает новый объектAsyncMock
.
Установка spec функции
Mock
илиMagicMock
в асинхронную функцию приведет к тому, что после вызова будет возвращен объект coroutine.>>> 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
(если родительский mock являетсяAsyncMock
илиMagicMock
) илиMock
(если родительский mock являетсяMock
). Все асинхронные функции будутAsyncMock
.>>> class ExampleClass: ... def sync_foo(): ... pass ... async def async_foo(): ... pass ... >>> a_mock = AsyncMock(ExampleClass) >>> a_mock.sync_foo <MagicMock name='mock.sync_foo' id='...'> >>> a_mock.async_foo <AsyncMock name='mock.async_foo' id='...'> >>> mock = Mock(ExampleClass) >>> mock.sync_foo <Mock name='mock.sync_foo' id='...'> >>> mock.async_foo <AsyncMock name='mock.async_foo' id='...'>
Добавлено в версии 3.8.
-
assert_awaited
()¶ Утверждение, что имитатор ожидался хотя бы один раз. Обратите внимание, что это происходит отдельно от вызова объекта, необходимо использовать ключевое слово
await
:>>> mock = AsyncMock() >>> async def main(coroutine_mock): ... await coroutine_mock ... >>> coroutine_mock = mock() >>> mock.called True >>> mock.assert_awaited() Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited. >>> asyncio.run(main(coroutine_mock)) >>> mock.assert_awaited()
-
assert_awaited_once
()¶ Утверждение, что насмешка была ожидаема ровно один раз.
>>> mock = AsyncMock() >>> async def main(): ... await mock() ... >>> asyncio.run(main()) >>> mock.assert_awaited_once() >>> asyncio.run(main()) >>> mock.method.assert_awaited_once() Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited once. Awaited 2 times.
-
assert_awaited_with
(*args, **kwargs)¶ Утверждает, что последний 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)¶ Утверждает, что мемок был ожидаем ровно один раз и с указанными аргументами.
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_once_with('foo', bar='bar') >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_once_with('foo', bar='bar') Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited once. Awaited 2 times.
-
assert_any_await
(*args, **kwargs)¶ Утверждает, что мемок был когда-либо ожидаем с указанными аргументами.
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> asyncio.run(main('hello')) >>> mock.assert_any_await('foo', bar='bar') >>> mock.assert_any_await('other') Traceback (most recent call last): ... AssertionError: mock('other') await not found
-
assert_has_awaits
(calls, any_order=False)¶ Утверждает, что имитатор был ожидаем указанными вызовами. Список
await_args_list
проверяется на наличие ожиданий.Если any_order равно false, то ожидания должны быть последовательными. Могут быть дополнительные вызовы до или после указанных ожиданий.
Если any_order равно true, то ожидания могут быть расположены в любом порядке, но все они должны появляться в
await_args_list
.>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> calls = [call("foo"), call("bar")] >>> mock.assert_has_awaits(calls) Traceback (most recent call last): ... AssertionError: Awaits not found. Expected: [call('foo'), call('bar')] Actual: [] >>> asyncio.run(main('foo')) >>> asyncio.run(main('bar')) >>> mock.assert_has_awaits(calls)
-
assert_not_awaited
()¶ Утверждайте, что насмешек никто не ждал.
>>> mock = AsyncMock() >>> mock.assert_not_awaited()
-
reset_mock
(*args, **kwargs)¶ См.
Mock.reset_mock()
. Также устанавливаетawait_count
в 0,await_args
в None, и очищаетawait_args_list
.
-
await_count
¶ Целое число, отслеживающее, сколько раз объект mock был ожидаем.
>>> mock = AsyncMock() >>> async def main(): ... await mock() ... >>> asyncio.run(main()) >>> mock.await_count 1 >>> asyncio.run(main()) >>> mock.await_count 2
-
await_args
¶ Это либо
None
(если имитатор не был ожидаем), либо аргументы, с которыми имитатор был ожидаем в последний раз. Функционирует так же, какMock.call_args
.>>> mock = AsyncMock() >>> async def main(*args): ... await mock(*args) ... >>> mock.await_args >>> asyncio.run(main('foo')) >>> mock.await_args call('foo') >>> asyncio.run(main('bar')) >>> mock.await_args call('bar')
-
await_args_list
¶ Это список всех ожиданий, сделанных для объекта-макета по порядку (длина списка равна количеству ожиданий). Пока не было сделано ни одного ожидания, это пустой список.
>>> mock = AsyncMock() >>> async def main(*args): ... await mock(*args) ... >>> mock.await_args_list [] >>> asyncio.run(main('foo')) >>> mock.await_args_list [call('foo')] >>> asyncio.run(main('bar')) >>> mock.await_args_list [call('foo'), call('bar')]
Звонок на¶
Макетные объекты можно вызывать. Вызов возвращает значение, заданное в качестве атрибута 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
также может быть любым итерируемым объектом. Повторные вызовы mock будут возвращать значения из итерируемого объекта (пока итерируемый объект не будет исчерпан и не возникнет ошибка StopIteration
):
>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
...
StopIteration
Если какие-либо члены iterable являются исключениями, они будут подняты, а не возвращены:
>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (most recent call last):
...
ValueError
>>> m()
66
Удаление атрибутов¶
Макетные объекты создают атрибуты по требованию. Это позволяет им притворяться объектами любого типа.
Вы можете захотеть, чтобы объект-макет возвращал False
на вызов hasattr()
или поднимал AttributeError
при получении атрибута. Вы можете сделать это, предоставив объект в качестве spec
для имитатора, но это не всегда удобно.
Вы «блокируете» атрибуты, удаляя их. После удаления доступ к атрибуту вызовет ошибку AttributeError
.
>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
...
AttributeError: f
Макетные имена и атрибут name¶
Поскольку «name» является аргументом конструктора Mock
, если вы хотите, чтобы у вашего объекта-макета был атрибут «name», вы не можете просто передать его во время создания. Есть две альтернативы. Один вариант - использовать configure_mock()
:
>>> mock = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'my_name'
Более простой вариант - просто установить атрибут «name» после создания макета:
>>> mock = MagicMock()
>>> mock.name = "foo"
Присоединение блоков в качестве атрибутов¶
Когда вы присоединяете имитатор в качестве атрибута другого имитатора (или в качестве возвращаемого значения), он становится «дочерним» по отношению к этому имитатору. Вызовы к дочернему объекту записываются в атрибуты method_calls
и mock_calls
родительского объекта. Это полезно для настройки дочерних имитаторов и последующего присоединения их к родительскому, или для присоединения имитаторов к родительскому, который записывает все вызовы дочерних и позволяет делать утверждения о порядке вызовов между имитаторами:
>>> parent = MagicMock()
>>> child1 = MagicMock(return_value=None)
>>> child2 = MagicMock(return_value=None)
>>> parent.child1 = child1
>>> parent.child2 = child2
>>> child1(1)
>>> child2(2)
>>> parent.mock_calls
[call.child1(1), call.child2(2)]
Исключением является случай, когда у подражателя есть имя. Это позволяет предотвратить «воспитание», если по какой-то причине вы не хотите, чтобы оно происходило.
>>> mock = MagicMock()
>>> not_a_child = MagicMock(name='not-a-child')
>>> mock.attribute = not_a_child
>>> mock.attribute()
<MagicMock name='not-a-child()' id='...'>
>>> mock.mock_calls
[]
Макеты, созданные для вас с помощью patch()
, автоматически получают имена. Чтобы прикрепить макеты, имеющие имена, к родителю, вы используете метод attach_mock()
:
>>> thing1 = object()
>>> thing2 = object()
>>> parent = MagicMock()
>>> with patch('__main__.thing1', return_value=None) as child1:
... with patch('__main__.thing2', return_value=None) as child2:
... parent.attach_mock(child1, 'child1')
... parent.attach_mock(child2, 'child2')
... child1('one')
... child2('two')
...
>>> parent.mock_calls
[call.child1('one'), call.child2('two')]
- 1
Единственным исключением являются магические методы и атрибуты (те, которые имеют ведущее и последующее двойное подчеркивание). Mock не создает их, а вместо этого выдает сообщение
AttributeError
. Это происходит потому, что интерпретатор часто неявно запрашивает эти методы и очень* путается, получая новый объект Mock, когда ожидает магический метод. Если вам нужна поддержка магических методов, смотрите magic methods.
Патчеры¶
Декораторы исправлений используются для исправления объектов только в пределах области действия функции, которую они украшают. Они автоматически выполняют распаковку за вас, даже если при этом возникают исключения. Все эти функции также можно использовать в операторах with или в качестве декораторов классов.
патч¶
Примечание
Главное - выполнять исправления в правильном пространстве имен. См. раздел where to patch.
-
unittest.mock.
patch
(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶ patch()
действует как декоратор функций, декоратор классов или менеджер контекста. Внутри тела функции или оператора with, цель исправляется на новый объект. При выходе из функции или оператора with исправление отменяется.Если new опущено, то target заменяется на
AsyncMock
, если исправляемый объект является асинхронной функцией, илиMagicMock
в противном случае. Еслиpatch()
используется в качестве декоратора и new опущено, то созданный mock передается в качестве дополнительного аргумента декорируемой функции. Еслиpatch()
используется в качестве менеджера контекста, созданный макет возвращается менеджером контекста.target должна быть строкой в форме
'package.module.ClassName'
. Объект target импортируется, а указанный объект заменяется объектом new, поэтому target должен быть импортируемым из среды, из которой вы вызываетеpatch()
. Цель импортируется при выполнении декорированной функции, а не во время декорирования.Аргументы ключевых слов spec и spec_set передаются в
MagicMock
, если patch создает его для вас.Дополнительно вы можете передать
spec=True
илиspec_set=True
, что заставит patch передать в качестве объекта spec/spec_set объект, над которым издеваются.new_callable позволяет вам указать другой класс или вызываемый объект, который будет вызван для создания нового объекта. По умолчанию
AsyncMock
используется для асинхронных функций иMagicMock
для остальных.Более мощной формой spec является autospec. Если вы установите значение
autospec=True
, то макет будет создан со спецификацией заменяемого объекта. Все атрибуты имитатора также будут иметь спецификацию соответствующего атрибута заменяемого объекта. У методов и функций, над которыми подражают, будут проверяться аргументы, и если они будут вызваны с неправильной сигнатурой, то будет выдано сообщениеTypeError
. Для макетов, заменяющих класс, их возвращаемое значение («экземпляр») будет иметь ту же спецификацию, что и класс. См. функцииcreate_autospec()
и Автоспецификация.Вместо
autospec=True
можно передатьautospec=some_object
, чтобы использовать произвольный объект в качестве спецификации вместо заменяемого.По умолчанию
patch()
не заменяет несуществующие атрибуты. Если вы передадитеcreate=True
, и атрибут не существует, patch создаст атрибут для вас при вызове исправляемой функции, и удалит его снова после выхода исправляемой функции. Это полезно для написания тестов против атрибутов, которые ваш производственный код создает во время выполнения. По умолчанию она выключена, так как это может быть опасно. Включив его, вы можете писать проходные тесты против API, которых на самом деле не существует!Примечание
Изменено в версии 3.5: Если вы патчите встроенные модули в модуле, то вам не нужно передавать
create=True
, они будут добавлены по умолчанию.Patch можно использовать как декоратор класса
TestCase
. Он работает путем декорирования каждого тестового метода в классе. Это уменьшает количество кода, когда ваши тестовые методы имеют общий набор патчей.patch()
находит тесты, ища имена методов, которые начинаются сpatch.TEST_PREFIX
. По умолчанию это'test'
, что соответствует способу поиска тестовunittest
. Вы можете указать альтернативный префикс, задавpatch.TEST_PREFIX
.Patch можно использовать в качестве менеджера контекста с помощью оператора with. В данном случае исправление применяется к блоку с отступом после оператора with. Если вы используете «as», то исправленный объект будет привязан к имени после «as»; очень полезно, если
patch()
создает для вас объект-макет.patch()
принимает произвольные аргументы в виде ключевых слов. Они будут переданы вAsyncMock
, если исправляемый объект является асинхронным, вMagicMock
в противном случае или в new_callable, если указано.patch.dict(...)
,patch.multiple(...)
иpatch.object(...)
доступны для альтернативных случаев использования.
patch()
как декоратор функции, создавая для вас имитатор и передавая его в декорируемую функцию:
>>> @patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
... print(mock_class is SomeClass)
...
>>> function(None)
True
Патчтинг класса заменяет класс на MagicMock
экземпляр. Если класс инстанцирован в тестируемом коде, то будет использоваться return_value
экземпляр.
Если класс инстанцируется несколько раз, можно использовать side_effect
, чтобы каждый раз возвращать новый mock. В качестве альтернативы вы можете установить возвращаемое_значение как угодно.
Чтобы настроить возвращаемые значения для методов экземпляров в исправленном классе, вы должны сделать это на 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 будет иметь тот же spec.
>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()
Аргумент new_callable полезен, когда вы хотите использовать класс, альтернативный классу по умолчанию 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()
создает для вас имитатор, обычно первое, что вам нужно сделать, это настроить имитатор. Некоторые из этих настроек могут быть выполнены в вызове патча. Любые произвольные ключевые слова, которые вы передадите в вызов, будут использованы для установки атрибутов созданного макета:
>>> patcher = patch('__main__.thing', first='one', second='two')
>>> mock_thing = patcher.start()
>>> mock_thing.first
'one'
>>> mock_thing.second
'two'
Помимо атрибутов созданного макета можно настроить атрибуты дочерних макетов, такие как return_value
и side_effect
. Синтаксически их нельзя передавать непосредственно в качестве аргументов ключевых слов, но словарь с такими ключами может быть расширен в вызов 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
(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶ сопоставить именованный член (атрибут) объекта (цель) с объектом-макетом.
patch.object()
может использоваться как декоратор, декоратор класса или менеджер контекста. Аргументы new, spec, create, spec_set, autospec и new_callable имеют то же значение, что и дляpatch()
. Как иpatch()
,patch.object()
принимает произвольные аргументы ключевых слов для настройки создаваемого имитационного объекта.При использовании в качестве декоратора класса
patch.object()
отдает честьpatch.TEST_PREFIX
в выборе методов для обертывания.
Вы можете вызвать patch.object()
с тремя аргументами или с двумя аргументами. Форма с тремя аргументами принимает объект для исправления, имя атрибута и объект, на который нужно заменить атрибут.
При вызове в форме с двумя аргументами вы опускаете заменяющий объект, и для вас создается макет, который передается в качестве дополнительного аргумента декорированной функции:
>>> @patch.object(SomeClass, 'class_method')
... def test(mock_method):
... SomeClass.class_method(3)
... mock_method.assert_called_with(3)
...
>>> test()
spec, create и другие аргументы для patch.object()
имеют то же значение, что и для patch()
.
patch.dict¶
-
patch.
dict
(in_dict, values=(), clear=False, **kwargs)¶ Исправьте словарь или объект, похожий на словарь, и восстановите словарь в исходное состояние после проверки.
in_dict может быть словарем или отображением, как контейнер. Если это отображение, то оно должно как минимум поддерживать получение, установку и удаление элементов, а также итерацию по ключам.
in_dict также может быть строкой, указывающей имя словаря, который будет получен при его импорте.
values может быть словарем значений для установки в словарь. values также может быть итерабельной системой пар
(key, value)
.Если clear равно true, то словарь будет очищен перед установкой новых значений.
patch.dict()
может также вызываться с произвольными аргументами в виде ключевых слов для установки значений в словаре.Изменено в версии 3.8:
patch.dict()
теперь возвращает исправленный словарь при использовании в качестве менеджера контекста.
patch.dict()
может использоваться как менеджер контекста, декоратор или декоратор класса:
>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
... assert foo == {'newkey': 'newvalue'}
>>> test()
>>> assert foo == {}
При использовании в качестве декоратора класса patch.dict()
отдает предпочтение patch.TEST_PREFIX
(по умолчанию 'test'
) при выборе методов для обертывания:
>>> import os
>>> import unittest
>>> from unittest.mock import patch
>>> @patch.dict('os.environ', {'newkey': 'newvalue'})
... class TestSample(unittest.TestCase):
... def test_sample(self):
... self.assertEqual(os.environ['newkey'], 'newvalue')
Если вы хотите использовать другой префикс для своего теста, вы можете сообщить патчерам о другом префиксе, установив значение patch.TEST_PREFIX
. Подробнее о том, как изменить значение смотрите в TEST_PREFIX.
patch.dict()
можно использовать для добавления членов в словарь, или просто позволить тесту изменить словарь, и убедиться, что словарь будет восстановлен после завершения теста.
>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
... assert foo == {'newkey': 'newvalue'}
... assert patched_foo == {'newkey': 'newvalue'}
... # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
... patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
... print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ
Ключевые слова могут быть использованы в вызове patch.dict()
для установки значений в словаре:
>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
... import mymodule
... mymodule.function('some', 'args')
...
'fish'
patch.dict()
можно использовать с объектами типа словарей, которые на самом деле словарями не являются. Как минимум, они должны поддерживать получение, установку, удаление элементов и либо итерацию, либо проверку принадлежности. Это соответствует магическим методам __getitem__()
, __setitem__()
, __delitem__()
и либо __iter__()
, либо __contains__()
.
>>> class Container:
... def __init__(self):
... self.values = {}
... def __getitem__(self, name):
... return self.values[name]
... def __setitem__(self, name, value):
... self.values[name] = value
... def __delitem__(self, name):
... del self.values[name]
... def __iter__(self):
... return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
... assert thing['one'] == 2
... assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']
patch.multiple¶
-
patch.
multiple
(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶ Выполнить несколько исправлений за один вызов. Он принимает объект для исправления (либо как объект, либо как строку для получения объекта путем импорта) и аргументы ключевых слов для исправлений:
with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): ...
Используйте
DEFAULT
в качестве значения, если вы хотите, чтобыpatch.multiple()
создавал макеты за вас. В этом случае созданные макеты передаются в декорированную функцию по ключевому слову, а словарь возвращается, когдаpatch.multiple()
используется в качестве менеджера контекста.patch.multiple()
может использоваться как декоратор, декоратор класса или менеджер контекста. Аргументы spec, spec_set, create, autospec и new_callable имеют то же значение, что и дляpatch()
. Эти аргументы будут применяться ко всеми исправлениям, выполненнымpatch.multiple()
.При использовании в качестве декоратора класса
patch.multiple()
отдает честьpatch.TEST_PREFIX
в выборе методов для обертывания.
Если вы хотите, чтобы patch.multiple()
создавал макеты за вас, то вы можете использовать DEFAULT
в качестве значения. Если вы используете patch.multiple()
в качестве декоратора, то созданные макеты передаются в декорируемую функцию по ключевому слову.
>>> thing = object()
>>> other = object()
>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
... assert isinstance(thing, MagicMock)
... assert isinstance(other, MagicMock)
...
>>> test_function()
patch.multiple()
может быть вложен с другими декораторами patch
, но помещает аргументы, передаваемые ключевым словом, после любого из стандартных аргументов, создаваемых patch()
:
>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
... assert 'other' in repr(other)
... assert 'thing' in repr(thing)
... assert 'exit' in repr(mock_exit)
...
>>> test_function()
Если в качестве менеджера контекста используется patch.multiple()
, то значение, возвращаемое менеджером контекста, представляет собой словарь, в котором созданные макеты имеют ключи по имени:
>>> with patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) as values:
... assert 'other' in repr(values['other'])
... assert 'thing' in repr(values['thing'])
... assert values['thing'] is thing
... assert values['other'] is other
...
методы исправления: запуск и остановка¶
Все патчеры имеют методы start()
и stop()
. Они упрощают выполнение исправлений в методах 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(unittest.TestCase):
... def setUp(self):
... self.patcher1 = patch('package.module.Class1')
... self.patcher2 = patch('package.module.Class2')
... self.MockClass1 = self.patcher1.start()
... self.MockClass2 = self.patcher2.start()
...
... def tearDown(self):
... self.patcher1.stop()
... self.patcher2.stop()
...
... def test_something(self):
... assert package.module.Class1 is self.MockClass1
... assert package.module.Class2 is self.MockClass2
...
>>> MyTest('test_something').run()
Осторожно
Если вы используете эту технику, вы должны убедиться, что исправление «отменено» вызовом stop
. Это может быть сложнее, чем вы думаете, потому что если в setUp
возникает исключение, то tearDown
не вызывается. unittest.TestCase.addCleanup()
делает это проще:
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... patcher = patch('package.module.Class')
... self.MockClass = patcher.start()
... self.addCleanup(patcher.stop)
...
... def test_something(self):
... assert package.module.Class is self.MockClass
...
В качестве дополнительного бонуса вам больше не нужно хранить ссылку на объект patcher
.
Также можно остановить все запущенные патчи, используя patch.stopall()
.
-
patch.
stopall
()¶ Остановить все активные патчи. Останавливает только патчи, запущенные с помощью
start
.
встраиваемые модули¶
Вы можете исправлять любые встроенные модули внутри модуля. Следующий пример исправляет встроенный модуль ord()
:
>>> @patch('__main__.ord')
... def test(mock_ord):
... mock_ord.return_value = 101
... print(ord('c'))
...
>>> test()
101
TEST_PREFIX¶
Все патчеры могут быть использованы в качестве декораторов класса. При таком использовании они оборачивают каждый тестовый метод класса. Патчеры распознают методы, начинающиеся с 'test'
, как тестовые. Это тот же способ, которым unittest.TestLoader
находит тестовые методы по умолчанию.
Возможно, вы захотите использовать другой префикс для своих тестов. Вы можете сообщить патчерам о другом префиксе, установив patch.TEST_PREFIX
:
>>> patch.TEST_PREFIX = 'foo'
>>> value = 3
>>>
>>> @patch('__main__.value', 'not three')
... class Thing:
... def foo_one(self):
... print(value)
... def foo_two(self):
... print(value)
...
>>>
>>> Thing().foo_one()
not three
>>> Thing().foo_two()
not three
>>> value
3
Декораторы матрешки¶
Если вы хотите выполнить несколько исправлений, вы можете просто сложить декораторы.
С помощью этого узора можно сложить несколько накладных декоративных элементов:
>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
... assert SomeClass.static_method is mock1
... assert SomeClass.class_method is mock2
... SomeClass.static_method('foo')
... SomeClass.class_method('bar')
... return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')
Обратите внимание, что декораторы применяются снизу вверх. Это стандартный способ применения декораторов в Python. Порядок созданных макетов, переданных в вашу тестовую функцию, соответствует этому порядку.
Где ставить заплатки¶
patch()
работает путем (временной) замены объекта, на который указывает имя 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')
Дескрипторы исправлений и прокси-объекты¶
И patch, и patch.object корректно исправляют и восстанавливают дескрипторы: методы класса, статические методы и свойства. Их следует исправлять на классе, а не на экземпляре. Они также работают с некоторыми объектами, которые проксируют доступ к атрибутам, например, django settings object.
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__
Числовые методы (включая варианты правой руки и in-place):
__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__
.Представление пути файловой системы:
__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, которые не имеют значения для невызываемого mock.
Магические методы настраиваются с помощью объектов 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
хранится изолированно от других.
Помощники¶
сентинел¶
-
unittest.mock.
sentinel
¶ Объект
sentinel
обеспечивает удобный способ предоставления уникальных объектов для ваших тестов.Атрибуты создаются по требованию, когда вы обращаетесь к ним по имени. Обращение к одному и тому же атрибуту всегда будет возвращать один и тот же объект. Возвращаемые объекты имеют разумный repr, чтобы сообщения об ошибках при тестировании были читаемы.
Иногда при тестировании необходимо проверить, что определенный объект передается в качестве аргумента другому методу или возвращается. Для проверки этого обычно создаются именованные объекты 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
для указания того, что должно использоваться обычное возвращаемое значение.
звоните на¶
-
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
представляет собой либо кортеж из (позиционные args, ключевые args), либо (имя, позиционные args, ключевые args) в зависимости от того, как он был построен. Когда вы создаете их сами, это не особенно интересно, но объекты call
, которые находятся в атрибутах Mock.call_args
, Mock.call_args_list
и Mock.mock_calls
, могут быть проанализированы, чтобы получить отдельные аргументы, которые они содержат.
Объекты call
в Mock.call_args
и Mock.call_args_list
представляют собой два кортежа (позиционные args, ключевые args), тогда как объекты call
в Mock.mock_calls
, а также те, которые вы конструируете сами, представляют собой три кортежа (имя, позиционные args, ключевые args).
Вы можете использовать их «кортежность» для извлечения отдельных аргументов для более сложного анализа и утверждений. Позиционные аргументы представляют собой кортеж (пустой кортеж, если нет позиционных аргументов), а аргументы ключевых слов - словарь:
>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> kall.args
(1, 2, 3)
>>> kall.kwargs
{'arg': 'one', 'arg2': 'two'}
>>> kall.args is kall[0]
True
>>> kall.kwargs is kall[1]
True
>>> m = MagicMock()
>>> m.foo(4, 5, 6, arg='two', arg2='three')
<MagicMock name='mock.foo()' id='...'>
>>> kall = m.mock_calls[0]
>>> name, args, kwargs = kall
>>> name
'foo'
>>> args
(4, 5, 6)
>>> kwargs
{'arg': 'two', 'arg2': 'three'}
>>> name is m.mock_calls[0][0]
True
create_autospec¶
-
unittest.mock.
create_autospec
(spec, spec_set=False, instance=False, **kwargs)¶ Создайте объект-макет, используя другой объект в качестве образца. Атрибуты макета будут использовать соответствующий атрибут объекта spec в качестве образца.
У функций или методов, над которыми насмехаются, будут проверяться аргументы, чтобы убедиться, что они вызываются с правильной сигнатурой.
Если spec_set имеет значение
True
, то при попытке установить атрибуты, не существующие в объекте spec, возникнет ошибкаAttributeError
.Если в качестве спецификации используется класс, то возвращаемое значение имитатора (экземпляр класса) будет иметь ту же спецификацию. Вы можете использовать класс в качестве спецификации для объекта экземпляра, передав
instance=True
. Возвращаемый имитатор будет вызываемым только в том случае, если экземпляры имитатора являются вызываемыми.create_autospec()
также принимает произвольные аргументы ключевых слов, которые передаются в конструктор созданного макета.
Примеры использования автоспецификации с помощью Автоспецификация и аргумента autospec в create_autospec()
смотрите в patch()
.
Изменено в версии 3.8: create_autospec()
теперь возвращает AsyncMock
, если цель является асинхронной функцией.
ЛЮБОЙ¶
-
unittest.mock.
ANY
¶
Иногда вам может понадобиться сделать утверждения о некоторых аргументах в вызове mock, но некоторые аргументы либо не важны, либо вы хотите вытащить их по отдельности из 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: Добавлено
__iter__()
в реализацию, чтобы итерация (например, в циклах for) правильно потребляла read_data.
Использование open()
в качестве менеджера контекста - отличный способ обеспечить правильное закрытие обработчиков файлов и становится все более распространенным:
with open('/some/path', 'w') as f:
f.write('something')
Проблема в том, что даже если вы высмеиваете вызов open()
, именно возвращенный объект используется в качестве менеджера контекста (и вызывает __enter__()
и __exit__()
).
Подсмеивание менеджеров контекста с помощью MagicMock
достаточно распространено и достаточно сложно, чтобы вспомогательная функция была полезной.
>>> m = mock_open()
>>> with patch('__main__.open', m):
... with open('foo', 'w') as h:
... h.write('some stuff')
...
>>> m.mock_calls
[call('foo', 'w'),
call().__enter__(),
call().write('some stuff'),
call().__exit__(None, None, None)]
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')
И для чтения файлов:
>>> with patch('__main__.open', mock_open(read_data='bibble')) as m:
... with open('foo') as h:
... result = h.read()
...
>>> m.assert_called_once_with('foo')
>>> assert result == 'bibble'
Автоспецификация¶
Автоспецификация основана на существующей функции spec
в mock. Она ограничивает api имитаторов api оригинального объекта (спецификации), но является рекурсивной (реализованной лениво), так что атрибуты имитаторов имеют только те же api, что и атрибуты спецификации. Кроме того, функции/методы имитаторов имеют ту же сигнатуру вызова, что и оригинальные, поэтому при неправильном вызове они выдают ошибку TypeError
.
Прежде чем я объясню, как работает автоспецификация, вот почему она необходима.
Mock
- это очень мощный и гибкий объект, но он страдает двумя недостатками, когда используется для подражания объектам тестируемой системы. Один из этих недостатков специфичен для Mock
api, а другой является более общей проблемой использования объектов-макетов.
Сначала проблема, специфичная для Mock
. У Mock
есть два метода assert, которые очень удобны: assert_called_with()
и assert_called_once_with()
.
>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
Traceback (most recent call last):
...
AssertionError: Expected 'mock' to be called once. Called 2 times.
Поскольку макеты автоматически создают атрибуты по требованию и позволяют вызывать их с произвольными аргументами, если вы неправильно напишете один из этих методов утверждения, то ваше утверждение пропадет:
>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assret_called_once_with(4, 5, 6) # Intentional typo!
Из-за опечатки ваши тесты могут пройти тихо и некорректно.
Вторая проблема более общая для мокинга. Если вы рефакторите часть вашего кода, переименуете члены и так далее, все тесты для кода, который все еще использует старый api, но использует моки вместо реальных объектов, все равно пройдут. Это означает, что все ваши тесты могут пройти, даже если ваш код сломан.
Обратите внимание, что это еще одна причина, по которой вам нужны интеграционные тесты, а также тесты модулей. Тестирование всего по отдельности - это хорошо и замечательно, но если вы не проверяете, как ваши модули «соединены вместе», остается много места для ошибок, которые тесты могли бы отловить.
В mock
уже есть функция, помогающая в этом, которая называется speccing. Если вы используете класс или экземпляр в качестве spec
для макета, то вы можете получить доступ к атрибутам макета только к тем, которые существуют в реальном классе:
>>> from urllib import request
>>> mock = Mock(spec=request.Request)
>>> mock.assret_called_with # Intentional typo!
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
Спецификация относится только к самому макету, поэтому у нас остается та же проблема с любыми методами на макете:
>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with() # Intentional typo!
Автоспецификация решает эту проблему. Вы можете либо передать autospec=True
в patch()
/ patch.object()
, либо использовать функцию create_autospec()
для создания макета со спецификацией. Если вы используете аргумент autospec=True
в patch()
, то в качестве объекта спецификации будет использоваться заменяемый объект. Поскольку спецификация выполняется «лениво» (спецификация создается по мере обращения к атрибутам макета), вы можете использовать ее с очень сложными или глубоко вложенными объектами (например, модулями, которые импортируют модули, которые импортируют модули) без большого ущерба для производительности.
Вот пример его использования:
>>> from urllib import request
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> request is mock_request
True
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>
Видно, что request.Request
имеет спецификацию. request.Request
принимает два аргумента в конструкторе (один из которых - self). Вот что произойдет, если мы попытаемся вызвать его неправильно:
>>> req = request.Request()
Traceback (most recent call last):
...
TypeError: <lambda>() takes at least 2 arguments (1 given)
Спецификация также применяется к инстанцированным классам (т.е. к возвращаемому значению специальных имитаторов):
>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>
Объекты Request
не являются вызываемыми, поэтому возвращаемое значение инстанцирования нашего насмешника request.Request
- это не вызываемый насмешник. Благодаря этой спецификации любые опечатки в наших утверждениях будут вызывать правильную ошибку:
>>> req.add_header('spam', 'eggs')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_with # Intentional typo!
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')
Во многих случаях вы просто сможете добавить autospec=True
к существующим вызовам patch()
, и тогда вы будете защищены от ошибок, связанных с опечатками и изменениями api.
Помимо использования autospec через patch()
существует create_autospec()
для создания автоспецифицированных макетов напрямую:
>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>
Однако это не лишено оговорок и ограничений, поэтому не является поведением по умолчанию. Чтобы узнать, какие атрибуты доступны для объекта-образца, autospec должен проинтроспектировать (получить доступ к атрибутам) образец. Когда вы просматриваете атрибуты на макете, под капотом происходит соответствующий просмотр исходного объекта. Если какой-либо из ваших специфицированных объектов имеет свойства или дескрипторы, которые могут вызвать выполнение кода, то вы не сможете использовать autospec. С другой стороны, гораздо лучше проектировать свои объекты так, чтобы интроспекция была безопасной 4.
Более серьезной проблемой является то, что часто атрибуты экземпляра создаются в методе __init__()
и вообще не существуют в классе. autospec не может знать о динамически созданных атрибутах и ограничивает api видимыми атрибутами.
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Существует несколько различных способов решения этой проблемы. Самый простой, но не обязательно наименее раздражающий способ - просто установить требуемые атрибуты на макете после создания. То, что autospec не позволяет вам получать атрибуты, не существующие в спецификации, не мешает вам установить их:
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a = 33
...
Существует более агрессивная версия spec и autospec, которая действительно предотвращает установку несуществующих атрибутов. Это полезно, если вы хотите убедиться, что ваш код устанавливает только действующие атрибуты, но, очевидно, это предотвращает данный конкретный сценарий:
>>> with patch('__main__.Something', autospec=True, spec_set=True):
... thing = Something()
... thing.a = 33
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Вероятно, лучшим способом решения проблемы является добавление атрибутов класса в качестве значений по умолчанию для членов экземпляра, инициализированных в __init__()
. Обратите внимание, что если вы устанавливаете атрибуты по умолчанию только в __init__()
, то предоставление их через атрибуты класса (разумеется, общие для экземпляров) тоже будет быстрее. Например.
class Something:
a = 33
Это поднимает еще один вопрос. Относительно распространено предоставление значения по умолчанию None
для членов, которые позже станут объектами другого типа. None
будет бесполезен как спецификация, потому что он не позволит вам получить доступ к любым атрибутам или методам этого объекта. Так как None
никогда не будет полезен как спецификация, и, вероятно, указывает на член, который обычно будет иметь другой тип, autospec не использует спецификацию для членов, которые установлены в None
. Это будут обычные макеты (хорошо - 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='...'>
Пломбирование макетов¶
-
unittest.mock.
seal
(mock)¶ Seal отключит автоматическое создание макетов при рекурсивном доступе к атрибуту запечатываемого макета или к любому из его атрибутов, которые уже являются макетами.
Если имитируемому объекту с именем или спецификацией присвоен атрибут, он не будет учитываться в цепочке уплотнения. Это позволяет предотвратить фиксацию части объекта-макета с помощью печати.
>>> mock = Mock() >>> mock.submock.attribute1 = 2 >>> mock.not_submock = mock.Mock(name="sample_name") >>> seal(mock) >>> mock.new_attribute # This will raise AttributeError. >>> mock.submock.attribute2 # This will raise AttributeError. >>> mock.not_submock.attribute2 # This won't raise.
Добавлено в версии 3.7.