unittest.mock
— библиотека mock объектов¶
Добавлено в версии 3.3.
Исходный код: Lib/unittest/mock.py
unittest.mock
- библиотека для тестирования в Python. Она позволяет заменять
тестируемые части системы mock объектами и делать утверждения о том, как
они были используемый.
unittest.mock
обеспечивает базовый класс Mock
, устраняющий необходимость
создания множества заглушек во всем наборе тестов. После выполнения действия
можно сделать утверждение о том, какие методы/ атрибуты были используемый, и
аргументы, с которыми они были вызваны. Можно также задать возвращает
значения и задать необходимые атрибуты обычным способом.
Кроме того, mock предоставляет patch()
декоратор, который
обрабатывает модуль патчи и атрибуты уровня класса в область видимости теста,
а также sentinel
для создания уникальных объектов. Примеры использования
Mock
, MagicMock
и краткое руководство см. в patch()
.
Макет очень прост в использовании и предназначен для использования с unittest
.
Макет основан на шаблоне «действие -> утверждение» вместо «запись ->
воспроизведение», используемый многими мокинг (mocking) фреймворков.
Имеется бэкпорт unittest.mock
для более ранних версий Python, доступный как
mock в PyPI.
Краткое руководство¶
Объекты Mock
и MagicMock
создают все атрибуты и методы при обращении к
ним и сохраняют сведения о том, как они были используемый. Их можно настроить,
чтобы указать возвращает значения или ограничить доступные атрибуты, а
затем сделать утверждения о том, как они были используемый:
>>> from unittest.mock import MagicMock
>>> thing = ProductionClass()
>>> thing.method = MagicMock(return_value=3)
>>> thing.method(3, 4, 5, key='value')
3
>>> thing.method.assert_called_with(3, 4, 5, key='value')
side_effect
позволяет выполнять побочные эффекты, включая создание исключения
при вызове mock:
>>> mock = 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 настраивает mock на получение его
спецификации из другого объекта. Попытка доступа к атрибуты или методам на
mock, которые не существуют на spec, завершится ошибкой с
AttributeError
.
patch()
декоратор / менеджер контекста упрощающий mock классы или
объектов в тестируемом модуле. Указанный объект будет заменен mock (или
другим объектом) во время теста и восстановлен по окончании теста:
>>> 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, в котором
декораторы применяются). Это означает, что снизу вверх, так что в приведенном
выше примере mock для module.ClassName1
проходит сначала.
При patch()
важно, чтобы объекты исправлялись в пространстве имен, где они
просматриваются. Обычно это просто, но для быстрого руководства читать
where to patch.
А также декоратор patch()
может быть используемый как менеджер контекст в
инструкция:
>>> 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
Мок поддерживает мокинг (mocking) над Python магические методы. Самый простой способ
использования магических методов - с MagicMock
классом. Это позволяет делать
такие вещи, как:
>>> mock = MagicMock()
>>> mock.__str__.return_value = 'foobarbaz'
>>> str(mock)
'foobarbaz'
>>> mock.__str__.assert_called_with()
Mock позволяет назначать функции (или другие сущности Mock) магическим
методам, и они будут вызываться соответствующим образом. Класс MagicMock
- это
просто макет вариант, который имеет все волшебные методы, созданные для вас (ну,
все полезные в любом случае).
Ниже приведен пример использования магических методов с обычным классом Mock:
>>> mock = Mock()
>>> mock.__str__ = Mock(return_value='wheeeeee')
>>> str(mock)
'wheeeeee'
Для обеспечения того, чтобы mock объекты в тестах имели тот же API, что и
заменяемые объекты, можно использовать auto-speccing. Автоопределение можно
выполнить с помощью аргумента autospec для патчи или функции
create_autospec()
. Автоспецикация создает mock объекты, имеющие те же
атрибуты и методы, что и заменяемые объекты, а любые функции и методы
(включая конструкторы) имеют те же сигнатура вызова, что и реальный объект.
Это гарантирует, что ваши моки будут отказывать так же, как и ваши производственные код, если они используемый неправильно:
>>> 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
является гибким mock объектом, предназначенным для замены
использования заглушек и двойных тестов по всему вашему коду. Моки
вызываются и создают атрибуты как новые моки при обращении к ним [1].
Доступ к одному и тому же атрибут всегда возвращает один и тот же
mock. Моки записывают, как вы их используете, позволяя делать
утверждения о том, что сделал с ними ваш код.
MagicMock
- это подкласс Mock
со всеми волшебными методами, заранее
созданными и готовыми к использованию. Существуют также варианты, не являющиеся
вызываемыми, которые полезны при работе с объектами, не являющимися вызываемыми:
NonCallableMock
и NonCallableMagicMock
Декораторы patch()
упрощают временную замену классов в конкретном модуле
Mock
объектом. По умолчанию patch()
создадим для вас MagicMock
.
Можно указать альтернативный класс Mock
, используя аргумент new_callable
для patch()
.
-
class
unittest.mock.
Mock
(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)¶ Создать новый объект
Mock
.Mock
принимает несколько необязательных аргументов, определяющих поведение объекта Mock:spec: это может быть либо список строк, либо существующий объект (класс или сущность), который действует как спецификация для mock объекта. Если передать объект, то список строки формируется вызовом dir на объекте (исключая неподдерживаемые магические атрибуты и методы). Доступ к любому атрибут, отсутствующему в этом списке, вызовет
AttributeError
.Если spec является объектом (а не списком строки), то
__class__
возвращает класс объекта spec. Это позволяет мокам пройтиisinstance()
тесты.spec_set: более строгий вариант spec. Если используется, попытка set или получить атрибут на mock, который отсутствует в переданном объекте, так как spec_set вызовет
AttributeError
.side_effect: функция, вызываемая всякий раз, когда вызывается Mock. См.
side_effect
атрибут. Используется для создания исключений или динамического изменения возвращает значения. Функция вызывается с теми же аргументами, что и mock, и если она не возвращаетDEFAULT
, возвращает значение этой функции используемый как возвращает значение.В качестве альтернативы side_effect может быть класс исключения или сущность. В этом случае при вызове mock возникает исключение.
Если side_effect является итерабельным, то каждый вызов mock будет возвращает следующий значение из итератора.
Можно очистить side_effect, установив для него значение
None
.return_value: значение возвращенный при вызове mock. По умолчанию это новый макет (созданный при первом доступе). Смотрите
return_value
атрибут.unsafe: по умолчанию, если какой-либо атрибут начинается с assert или assret вызывает
AttributeError
. Прохождениеunsafe=True
позволит получить доступ к этим атрибуты.Добавлено в версии 3.5.
wraps: элемент для переноса объекта mock. Если wraps нет
None
то вызов макета передаст вызов обернутому объекту (возвращающему реальный результат). Доступ к атрибуту на mock будет возвращает объект Mock, который оборачивает соответствующий атрибут обернутого объекта (поэтому попытка доступа к несуществующему атрибут приведет к появлениюAttributeError
).Если mock имеет явный набор return_value, вызовы не передаются заключенному объекту и вместо этого return_value возвращенный.
name: если у mock есть название, то оно будет используемый в репр mock. Это может быть полезно для отладки. Имя распространяется на дочерние моки.
Моки (mocks) также могут вызываться с произвольными ключевыми аргументами. Они будут используемый для установки атрибуты на mock после его создания. Дополнительные сведения см. в описании метода
configure_mock()
.-
assert_called
()¶ Утверждать, что mock вызывался хотя бы один раз.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called()
Добавлено в версии 3.6.
-
assert_called_once
()¶ Утверждать, что mock вызывался ровно один раз.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() Traceback (most recent call last): ... AssertionError: Expected 'method' to have been called once. Called 2 times.
Добавлено в версии 3.6.
-
assert_called_with
(*args, **kwargs)¶ Метод является удобным способом подтверждения того, что последний вызов был выполнен определенным способом:
>>> mock = Mock() >>> mock.method(1, 2, 3, test='wow') <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_with(1, 2, 3, test='wow')
-
assert_called_once_with
(*args, **kwargs)¶ Утверждать, что mock был вызван ровно один раз и что этот вызов был с указанными аргументами.
>>> mock = Mock(return_value=None) >>> mock('foo', bar='baz') >>> mock.assert_called_once_with('foo', bar='baz') >>> mock('other', bar='values') >>> mock.assert_called_once_with('other', bar='values') Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times.
-
assert_any_call
(*args, **kwargs)¶ Утверждение, что mock был вызван с указанными аргументами.
Утверждение проходит, если mock когда-либо был вызван, в отличие от
assert_called_with()
иassert_called_once_with()
, которые проходят, только если вызов является самым последним, и в случаеassert_called_once_with()
он также должен быть единственным вызовом.>>> mock = Mock(return_value=None) >>> mock(1, 2, arg='thing') >>> mock('some', 'thing', 'else') >>> mock.assert_any_call(1, 2, arg='thing')
-
assert_has_calls
(calls, any_order=False)¶ Утверждение, что mock был вызван с указанными вызовами. Список
mock_calls
проверяется на наличие вызовов.Если any_order равно false, то вызовы должны быть последовательными. Могут быть дополнительные вызовы до или после указанных вызовов.
Если any_order true, то вызовы могут быть в любом порядке, но все они должны отображаться в
mock_calls
.>>> 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.
-
assert_not_called
()¶ Утверждать, что mock так и не был вызван.
>>> m = Mock() >>> m.hello.assert_not_called() >>> obj = m.hello() >>> m.hello.assert_not_called() Traceback (most recent call last): ... AssertionError: Expected 'hello' to not have been called. Called 1 times.
Добавлено в версии 3.5.
-
reset_mock
(*, return_value=False, side_effect=False)¶ Метод reset_mock сбрасывает все атрибуты вызова для объекта mock:
>>> mock = Mock(return_value=None) >>> mock('hello') >>> mock.called True >>> mock.reset_mock() >>> mock.called False
Изменено в версии 3.6: Добавлен только два аргумента ключевой в функцию reset_mock.
Это может быть полезно, если требуется создать ряд утверждений, которые повторно используют один и тот же объект. Обратите внимание, что
reset_mock()
не очищает возвращает значение,side_effect
или любой дочерний атрибуты, заданный с помощью обычного назначения по умолчанию. Если требуется сбросить return_value илиside_effect
, передайте соответствующий параметр какTrue
. Дочерние моки и возвращает значение mock (если таковые имеются) также сбрасываются.Примечание
return_value, и
side_effect
только ключевой аргумент.
-
mock_add_spec
(spec, spec_set=False)¶ Добавление spec в mock. spec может быть объектом или списком строки. В качестве атрибуты из spec можно выбрать только атрибуты на mock.
Если spec_set true, то можно установить только атрибуты на spec.
-
attach_mock
(mock, attribute)¶ Присоединить mock как атрибут этого, заменив его имя и родительский элемент. Вызовы присоединенному mock будут зарегистрированы в
method_calls
иmock_calls
атрибуты этого.
-
configure_mock
(**kwargs)¶ Установить атрибуты на mock с помощью ключевой аргументов.
Атрибуты плюс возвращает значения и побочные эффекты могут быть установлены на дочерних моков (mocks)х с помощью стандартной точечной нотации и распаковки словаря в вызове метода:
>>> 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()
существует для упрощения настройки после создания mock.
-
__dir__
()¶ Mock
объекты ограничивают результатыdir(some_mock)
полезными результатами. Для мок (mocks) с spec сюда входят все разрешенные атрибуты для mock.Узнайте
FILTER_DIR
, что делает эта фильтрация и как ее отключить.
-
_get_child_mock
(**kw)¶ Создать дочерние моки для атрибуты и возвращает значение. По умолчанию дочерние моки имеют тот же тип, что и родительские. Подклассы Mock могут захотеть переопределить это, чтобы настроить способ создания дочерних моков (mocks).
Для не вызываемых моков (mocks) будет используемый вызываемый вариант (а не любой пользовательский подкласс).
-
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() >>> 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
¶ Это может быть либо функция, вызываемая при вызове mock, либо итерабельная функция, либо исключение (класс или сущность).
Если передать в функцию, то её вызовут с теми же аргументами как mock и если функция возвращает единичный предмет
DEFAULT
требование к mock не будет тогда возвращает вообще функция возвращает. Если функция возвращаетDEFAULT
то mock будет возвращает нормальное значение (отreturn_value
).При передаче итерируемого, используется получаемый итератор, который должен yield значением при каждом вызове. Этот значение может быть либо сущность исключения, который должен быть создан, либо значение, который должен быть возвращенный из вызова в mock (обработка
DEFAULT
идентична варианту функции).Пример mock, который вызывает исключение (для проверки обработки исключения 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
(если mock не был вызван), либо аргументы, с которыми последний раз вызывался mock. Это будет в виде кортежа: первый член, к которому также можно получить доступ через свойство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
. Это кортежи, так что они могут быть распакованы, чтобы получить отдельные аргументы и сделать более сложные утверждения. Смотрите вызовы как кортежи.Изменено в версии 3.8: Добавлены свойства
args
иkwargs
.
-
call_args_list
¶ Список всех вызовов, выполненных для объекта mock в последовательности (таким образом, длина списка - это количество вызовов). Перед выполнением каких-либо вызовов это пустой список. Объект
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
¶ Помимо отслеживания вызовов к себе, моки (mocks) также отслеживают вызовы к методам и атрибуты, а также их методы и атрибуты:
>>> 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, его методы, магические методы и возвращает значение моков (mocks).>>> mock = MagicMock() >>> result = mock(1, 2, 3) >>> mock.first(a=3) <MagicMock name='mock.first()' id='...'> >>> mock.second() <MagicMock name='mock.second()' id='...'> >>> int(mock) 1 >>> result(1) <MagicMock name='mock()()' id='...'> >>> expected = [call(1, 2, 3), call.first(a=3), call.second(), ... call.__int__(), call()(1)] >>> mock.mock_calls == expected True
Члены
mock_calls
являютсяcall
объектами. Они могут быть распакованы как кортежи, чтобы получить отдельные аргументы. Смотрите вызовы как кортежи.Примечание
Способ записи
mock_calls
означает, что при выполнении вложенных вызовов параметры предков не записываются и поэтому всегда сравниваются равными:>>> mock = MagicMock() >>> mock.top(a=3).bottom() <MagicMock name='mock.top().bottom()' id='...'> >>> mock.mock_calls [call.top(a=3), call.top().bottom()] >>> mock.mock_calls[-1] == call.top(a=-1).bottom() True
-
__class__
¶ Обычно
__class__
атрибут объекта будет возвращать его тип. Для mock объекта сspec
,__class__
возвращает класс spec вместо этого. Это позволяет mock объектам пройтиisinstance()
тесты для заменяемого объекта/маскируемого как:>>> mock = Mock(spec=3) >>> isinstance(mock, int) True
__class__
присваивается, это позволяет mock пройти проверкуisinstance()
без необходимости использования spec:>>> 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
классах есть поддержка mocking магических методов. Подробные
сведения см. в разделе магические методы.
Классы mock и декораторы patch()
принимают произвольные аргументы
ключевой для конфигурации. Для patch()
декораторов ключевые слова
передаются конструктору создаваемого mock. Аргументы ключевой
предназначены для настройки атрибутов mock:
>>> 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
Вызываемый mock, созданный с помощью spec (или spec_set), выполнит интроспекцию сигнатуры объекта спецификации при сопоставлении вызовов mock. Поэтому он может совпадать с аргументами фактического вызова независимо от того, были ли они переданы позиционно или по имени:
>>> 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()
. При
Автоспецикация он также применяется к вызовам метода для объекта mock.
Изменено в версии 3.4: Добавлена сигнатура самоанализа по специфицированным и автоэкспецированным объектам mock.
-
class
unittest.mock.
PropertyMock
(*args, **kwargs)¶ Mock, предназначенный для использования в качестве свойства или другого дескриптора класса.
PropertyMock
предоставляет методы__get__()
и__set__()
, чтобы можно было указать возвращает значение при выборке.Получение
PropertyMock
сущность от объекта вызывает mock без аргументов. Установка этого параметра вызывает mock с установленным значением.:>>> 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)]
Из-за способа хранения mock атрибуты невозможно непосредственно
присоединить PropertyMock
к объекту mock. Вместо этого его можно
присоединить к объекту типа mock:
>>> 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)¶ Асинхронная версия
Mock
. ОбъектAsyncMock
будет вести себя так, что объект распознается как асинхронная функция, а результат вызова является ожидаемым.>>> mock = AsyncMock() >>> asyncio.iscoroutinefunction(mock) True >>> inspect.isawaitable(mock()) # doctest: +SKIP True
Результатом
mock()
является асинхронная функция, которая будет иметь результатside_effect
илиreturn_value
после того, как она была ожидаема:- если
side_effect
является функцией, асинхронная функция будет возвращать результат этой функции, - если
side_effect
является исключением, асинхронная функция вызывает исключение, - если
side_effect
является итераблем, асинхронная функция будет возвращает следующий значение итерабля, однако, если последовательность результатов исчерпана,StopAsyncIteration
поднимается немедленно, - если
side_effect
не определен, асинхронная функция будет возвращает значение, определенныйreturn_value
, следовательно, по умолчанию асинхронная функция возвращает новый объектAsyncMock
.
Установка асинхронной функции для spec
Mock
илиMagicMock
приведет к возвращенный объекта корутины после вызова.>>> async def async_func(): pass ... >>> mock = MagicMock(async_func) >>> mock <MagicMock spec='function' id='...'> >>> mock() # doctest: +SKIP <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
()¶ Утверждать, что mock ждали хотя бы один раз. Обратите внимание на то, что это отдельно от вызванного объекта,
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 ждали ровно один раз.
>>> 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 ждали ровно раз и с указанными аргументами.
>>> 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 когда-либо ждали с указанными аргументами.
>>> 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)¶ Утверждать, что mock ожидался с указанными вызовами. Список
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 никогда не ждали (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 не ожидался), либо аргументы, с которыми mock ожидал в последний раз. Выполняет те же функции, что и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
¶ Список всех awaits, сделанных для mock объекта в последовательности (поэтому длина списка - это количество awaited). Прежде чем будут сделаны какие-либо awaits, это пустой список.
>>> mock = AsyncMock() >>> async def main(*args): ... await mock(*args) ... >>> mock.await_args_list [] >>> asyncio.run(main('foo')) >>> mock.await_args_list [call('foo')] >>> asyncio.run(main('bar')) >>> mock.await_args_list [call('foo'), call('bar')]
- если
Вызов¶
Mock объекты вызываются. Вызов будет возвращаеть установленное значение в
качестве return_value
атрибута. Возвращаемое значение по умолчанию - новый
Mock объект; он создается при первом обращении к возвращает значение (либо
явным образом, либо путем вызова Mock) - он сохраняется и каждый раз
возвращается одино и тот же.
Вызовы объекта будут записываться в такие атрибуты, как call_args
и
call_args_list
.
Если side_effect
установлен, то он будет вызван после записи вызова, поэтому,
если side_effect
вызывает исключение, вызов по-прежнему записывается.
Самый простой способ создать исключение при вызове mock - сделать
side_effect
классом исключения или сущность:
>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]
Если side_effect
является функцией, то независимо от этой функции возвращает это
то, что вызывает mock возвращает. Функция side_effect
вызывается с теми
же аргументами, что и mock. Это позволяет динамически изменять
возвращаемое значение вызова на основе ввода:
>>> def side_effect(value):
... return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]
Если требуется, чтобы mock по-прежнему возвращал возвращаемое значение
(новый mock) или любое множество возвращаемых значений, это можно сделать
двумя способами. Либо возвращает mock.return_value
изнутри side_effect
, либо
возвращает DEFAULT
:
>>> m = MagicMock()
>>> def side_effect(*args, **kwargs):
... return m.return_value
...
>>> m.side_effect = side_effect
>>> m.return_value = 3
>>> m()
3
>>> def side_effect(*args, **kwargs):
... return DEFAULT
...
>>> m.side_effect = side_effect
>>> m()
3
Чтобы удалить side_effect
и возвращает поведение по умолчанию, установите для
side_effect
значение None
:
>>> m = MagicMock(return_value=6)
>>> def side_effect(*args, **kwargs):
... return 3
...
>>> m.side_effect = side_effect
>>> m()
3
>>> m.side_effect = None
>>> m()
6
Также side_effect
может быть любым итерабельным объектом. Повторные вызовы
mock будут возвращает значения из итерабла (до тех пор, пока итерабль
не будет исчерпан и не будет поднято StopIteration
):
>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
...
StopIteration
Если какие-либо члены iterable являются исключениями, они будут подняты вместо возвращения:
>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (most recent call last):
...
ValueError
>>> m()
66
Удаление атрибутов¶
Mock объекты создают атрибуты по требованию. Это позволяет им притворяться объектами любого типа.
Вы можете хотеть объект mock возвращаемый False
вызываемый
hasattr()
или поднять AttributeError
, когда атрибут принесен. Это можно
сделать, предоставив объект в качестве spec
для mock, но это не
всегда удобно.
Вы «блокируете» атрибуты, удаляя их. После удаления доступ к атрибуту
поднимет AttributeError
.
>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
...
AttributeError: f
Mock имена и имя атрибута¶
Поскольку «name» является аргументом конструктора Mock
, если требуется,
чтобы объект mock имел «name» атрибут его нельзя просто передать во
время создания. Есть две альтернативы. Одним из вариантов является использование
configure_mock()
:
>>> mock = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'my_name'
Более простой вариант - просто задать атрибут «name» после создании mock:
>>> mock = MagicMock()
>>> mock.name = "foo"
Присоединение моков (mocks) в качестве атрибутов¶
При прикреплении mock как атрибут другого mock (или как
возвращаемое значение) он становится «потомком» этого mock. Вызовы
ребенка зарегистрированы в method_calls
и mock_calls
атрибутах родителя. Это
полезно для настройки дочерних моков (mocks) и последующего присоединения их к
родительскому элементу или для присоединения моков (mocks) к родительскому
элементу, который записывает все вызовы к дочерним элементам и позволяет делать
утверждения о порядке вызовов между моками:
>>> parent = MagicMock()
>>> child1 = MagicMock(return_value=None)
>>> child2 = MagicMock(return_value=None)
>>> parent.child1 = child1
>>> parent.child2 = child2
>>> child1(1)
>>> child2(2)
>>> parent.mock_calls
[call.child1(1), call.child2(2)]
Исключение составляет случай, если у mock есть имя. Это позволяет предотвратить «воспитание», если по какой-то причине вы не хотите, чтобы это произошло.
>>> mock = MagicMock()
>>> not_a_child = MagicMock(name='not-a-child')
>>> mock.attribute = not_a_child
>>> mock.attribute()
<MagicMock name='not-a-child()' id='...'>
>>> mock.mock_calls
[]
Моки созданные для вас patch()
, автоматически присваиваются имена. Для
присоединения моков (mocks) с именами к родительскому элементу используется метод
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,
когда он ожидает магический метод. Если вам нужна поддержка волшебного метода,
обратитесь к магические методы. |
patchers¶
Декораторы patch используемые для патчинга объектов только в область видимости функции, которую они декорируют. Они автоматически обрабатывают распаковку для вас, даже если создаются исключения. Все эти функции также могут быть используемый в инструкции или в качестве декораторов классов.
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 пропущен, то целевой объект заменяется
AsyncMock
, если пропатченный объект является асинхронной функцией илиMagicMock
в противном случае. Еслиpatch()
используемый как декоратор и new пропущен, созданный mock передается в качестве дополнительного аргумента в декорированную функцию. Еслиpatch()
- используемый как менеджер контекста, созданный mock - возвращенный менеджером контекста.target должен быть строкой в форме
'package.module.ClassName'
. target импортируется, и указанный объект заменяется new объектом, поэтому target должен быть импортирован из среды, из которой выполняется вызовpatch()
. Целевой объект импортируется при выполнении декорированной функции, а не во время декорирования.Ключевые аргументы spec и spec_set переданы к
MagicMock
, если патч создает один для вас.Кроме того, можно передать
spec=True
илиspec_set=True
, что приводит к передаче патча в объект, который мокает как над spec/spec_set объектом.new_callable позволяет указать другой класс или вызываемый объект, который будет вызван для создания new объекта. По умолчанию
AsyncMock
используемый для асинхронных функций иMagicMock
для остальных.Более мощной формой spec является autospec. Если задать
autospec=True
, mock будет создан с spec заменяемого объекта. Все атрибуты mock также будут иметь spec соответствующего атрибут заменяемого объекта. Методы и функции, которые мокаются проверяются и вызываютTypeError
, если они вызваны с неправильной сигнатурой. Для имитаторов, заменяющих класс, их возвращает значение („сущность“) будет иметь те же spec, что и класс. См. описание функции иcreate_autospec()
Автоспецикация.Вместо
autospec=True
можно передатьautospec=some_object
для использования произвольного объекта в качестве spec вместо заменяемого.По умолчанию
patch()
не смогут заменить несуществующие атрибуты. Если вы передаетеcreate=True
, а атрибут не существует, патч создаст для вас атрибут при вызове исправленной функции и снова удалит ее после выхода из исправленной функции. Это полезно для написания тестов по сравнению с атрибуты, создаваемыми производственным код во время выполнения. Он выключен по умолчанию, поскольку может быть опасным. С его включенным можно писать проходящие тесты против API, которые на самом деле не существуют!Примечание
Изменено в версии 3.5: Если вы исправляете сборки в модуле, то вам не нужно передавать
create=True
, он будет добавлен по умолчанию.Патч можно используем как декоратор класса
TestCase
. Он работает, декорируя каждый метод тестирования в классе. Это уменьшает код шаблона, когда методы тестирования используют общий набор патчей.patch()
находит тесты путем поиска имен методов, начинающихся сpatch.TEST_PREFIX
. По умолчанию это значение равно'test'
, что соответствует способуunittest
поиска тестов. Можно указать альтернативный префикс, задавpatch.TEST_PREFIX
.Патч можно использовать в качестве менеджера контекста с помощью инструкции. Здесь патчинг применяется к блоку с отступом после блока с инструкция. При использовании «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 на любое желаемое.
Чтобы настроить возвращает значения на методы instances в пропатченном
классе, необходимо выполнить это на 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()
заменяет class, то возвращаемое
значение созданного mock будет иметь тот же spec.:
>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()
Аргумент new_callable полезен, если требуется использовать альтернативный класс
MagicMock
по умолчанию для созданного mock. Например, если вы хотите,
чтобы NonCallableMock
был используемый:
>>> thing = object()
>>> with patch('__main__.thing', new_callable=NonCallableMock) as mock_thing:
... assert thing is mock_thing
... thing()
...
Traceback (most recent call last):
...
TypeError: 'NonCallableMock' object is not callable
Другим примером использования может быть замена объекта io.StringIO
сущность:
>>> from io import StringIO
>>> def foo():
... print('Something')
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
... foo()
... assert mock_stdout.getvalue() == 'Something\n'
...
>>> test()
Когда patch()
создает mock для вас, обычно первым делом нужно
настроить mock. Некоторые из этих настроек могут быть выполнены при
вызове patch. Любые произвольные ключевые слова, передаваемые в вызов,
будут используемый для установки атрибуты на созданном mock:
>>> patcher = patch('__main__.thing', first='one', second='two')
>>> mock_thing = patcher.start()
>>> mock_thing.first
'one'
>>> mock_thing.second
'two'
Кроме того, можно настроить атрибуты созданных mock атрибутов, как
и return_value
и side_effect
, дочерних моков (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
По умолчанию попытка патча функции в несуществующем модуле (или методе или
атрибуте классп) завершится ошибкой с 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'
но добавление create=True
в вызов patch()
приведет к тому, что предыдущий
пример будет работать так, как ожидалось:
>>> @patch('sys.non_existing_attribute', 42, create=True)
... def test(mock_stdout):
... assert sys.non_existing_attribute == 42
...
>>> test()
patch.object¶
-
patch.
object
(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶ Патч именованного элемента (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()
.
patch.dict¶
-
patch.
dict
(in_dict, values=(), clear=False, **kwargs)¶ Патч словаря или словаря, подобного объекту и восстановление исходного состояние словаря после теста.
in_dict может быть словарь или контейнер типа отображения. Если он сопоставление, оно должно, по крайней мере, поддерживать получение, настройку и удаление элементов, а также итерацию по ключам.
in_dict может быть также строка, указывающий имя словаря, которое затем будет выбрано путем импорта.
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'}
... # Вы можете добавлять, обновлять или удалять ключи foo (или patched_foo, это одно и то же)
... patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
... print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ
Ключевые слова можно используемый в patch.dict()
вызове для установки значения
в словаре:
>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
... import mymodule
... mymodule.function('some', 'args')
...
'fish'
patch.dict()
можно используемый с такими словарями, как объекты, которые на самом
деле не являются словарями. Как минимум, они должны поддерживать получение,
настройку, удаление и итерацию или тест членства. Это соответствует магическим
методам __getitem__()
, __setitem__()
, __delitem__()
и либо __iter__()
, либо __contains__()
.
>>> class Container:
... def __init__(self):
... self.values = {}
... def __getitem__(self, name):
... return self.values[name]
... def __setitem__(self, name, value):
... self.values[name] = value
... def __delitem__(self, name):
... del self.values[name]
... def __iter__(self):
... return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
... assert thing['one'] == 2
... assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']
patch.multiple¶
-
patch.
multiple
(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶ Выполнение нескольких патчей в одном вызове. Требуется, чтобы объект был пропатчен (либо как объект, либо как строка, чтобы получить объект путем импорта) и ключевые аргументы для патчей:
with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): ...
Используйте
DEFAULT
в качестве значение, если вы хотите, чтобыpatch.multiple()
создавал для вас моки. В этом случае созданные моки передаются в декорированную функцию по ключу и возвращаемому словарю , когдаpatch.multiple()
используемый в качестве менеджера контекста.patch.multiple()
может быть использован как декоратор, декоратор класса или менеджер контекста. Аргументы spec, spec_set, create, autospec и new_callable имеют то же значение, что и дляpatch()
. Эти аргументы будут применены к всем патчам выполненнымpatch.multiple()
.При используемый в качестве декоратора класса
patch.multiple()
чтитpatch.TEST_PREFIX
за выбор методов обертывания.
Если вы хотите, чтобы patch.multiple()
создавал для вас моки, то вы можете
использовать DEFAULT
в качестве значения. При использовании patch.multiple()
в
качестве декоратора созданные моки передаются в декорированную функцию по
ключевому.:
>>> thing = object()
>>> other = object()
>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
... assert isinstance(thing, MagicMock)
... assert isinstance(other, MagicMock)
...
>>> test_function()
patch.multiple()
может быть вложен в другие patch
декораторы, но поставить
аргументы, переданные ключевой после любого из стандартных аргументов,
созданных patch()
:
>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
... assert 'other' in repr(other)
... assert 'thing' in repr(thing)
... assert 'exit' in repr(mock_exit)
...
>>> test_function()
Если patch.multiple()
используемый как менеджер контекста, то значение, возвращенный
менеджером контексвт, представляет собой словарь, в котором созданные моки
задаются по имени:
>>> with patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) as values:
... assert 'other' in repr(values['other'])
... assert 'thing' in repr(values['thing'])
... assert values['thing'] is thing
... assert values['other'] is other
...
Методы patch: start и stop¶
Все патчеры имеют start()
и stop()
методы. Это упрощает исправление в
setUp
методах или в тех случаях, когда требуется выполнить несколько
исправлений без вложенных декораторов или с помощью инструкции.
Чтобы использовать их, вызовите функцию patch()
, patch.object()
или patch.dict()
как обычную и сохраните ссылку на объект возвращенный patcher
. Затем можно
вызвать start()
, чтобы установить патч и stop()
отменить его.
Если для создания mock используется 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 применяет декораторы. Порядок созданных моков (mocks), переданных в тестовую функцию, соответствует этому порядку.
Что патчить?¶
patch()
работает путем (временного) изменения объекта, на который указывает
name, на другой объект. Может быть много имен, указывающих на любой
отдельный объект, поэтому для работы патчи необходимо убедиться, что вы
исправите имя, используемый тестируемой системой.
Основной принцип состоит в том, что выполняется патчинг на месте найденного вверху объекта, которое необязательно совпадает с местом его определения. Прояснить это поможет пара примеров.
Представьте, что у нас есть проект, который мы хотим протестировать со следующей структурой:
a.py
-> Defines SomeClass
b.py
-> from a import SomeClass
-> some_function instantiates SomeClass
Теперь мы хотим проверить some_function
но мы хотим mock SomeClass
используя patch()
. Проблема в том, что когда мы импортируем модуль b, который
мы должны будем сделать, то он импортирует SomeClass
из модуля A. Если мы
используем patch()
, чтобы mock a.SomeClass
то это не будет иметь
влияния на наш тест; модуль b уже имеет ссылку на реальный SomeClass
и
похоже, что наш патч не имел эффекта.
Ключ заключается в патче SomeClass
, где он используемый (или где он
просматривается). В этом случае some_function
будем искать SomeClass
в модуле b,
куда мы импортировали его. Патчинг должен выглядеть как:
@patch('b.SomeClass')
Однако рассмотреть альтернативный сценарий, где вместо модуля from a import SomeClass
b
делает import a
, и some_function
использует a.SomeClass
. Обе эти формы импорта
являются общими. В этом случае класс, который мы хотим патчить,
просматривается в модуле, поэтому мы должны исправить a.SomeClass
:
@patch('a.SomeClass')
Патчинг дескрипторов и прокси объекты¶
И patch, и patch.object правильно исправляют и восстанавливают дескрипторы: методы классов, статические методы и свойства. Вы должны патчить их в классе, а не на сущности. Они также работают с некоторыми объектами, которые прокси атрибут доступ, как объект настроек django.
MagicMock и поддержка магического метода¶
Мокинг магических методов¶
Mock
поддерживает мокинг (mocking) над методами Python протокола, также
известными как «магические методы». Это позволяет mock объектам заменять
контейнеры или другие объекты, реализующие Python протоколы.
Поскольку волшебные методы просматриваются иначе, чем обычные методы [2], эта поддержка была специально реализована. Это означает, что поддерживаются только конкретные магические методы. Поддерживаемый список включает почти все из них. Если есть какие-либо недостающие, которые вам нужны, пожалуйста, дайте нам знать.
Вы mock магические методы, установив интересующий вас метод в функцию или
mock сущность. Если используется функция, она должна принимать
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 для создания mock
попытка задать магический метод, отсутствующий в 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__
and__invert__
- Числовые методы (включая варианты правой руки и на месте):
__add__
,__sub__
,__mul__
,__matmul__
,__div__
,__truediv__
,__floordiv__
,__mod__
,__divmod__
,__lshift__
,__rshift__
,__and__
,__xor__
,__or__
, and__pow__
- Числовые методы преобразования:
__complex__
,__int__
,__float__
и__index__
- Методы дескриптора:
__get__
,__set__
и__delete__
- Pickling:
__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__
Волшебный 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, которые не имеют значения на не вызываемом 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__
: хэш по умолчанию для mock__str__
: по умолчанию str для mock__sizeof__
: по умолчанию sizeof для mock
Например:
>>> mock = MagicMock()
>>> int(mock)
1
>>> len(mock)
0
>>> list(mock)
[]
>>> object() in mock
False
Два метода равенства, __eq__()
и __ne__()
, являются особыми. Они выполняют
сравнение равенства по умолчанию для идентичности, используя side_effect
атрибут, если вы не измените их возвращает значение, чтобы вернуть
что-то другое:
>>> MagicMock() == 3
False
>>> MagicMock() != 3
True
>>> mock = MagicMock()
>>> mock.__eq__.return_value = True
>>> mock == 3
True
возвращает значение MagicMock.__iter__()
может быть любым итеративным объектом и не
обязательно должен быть итератором:
>>> mock = MagicMock()
>>> mock.__iter__.return_value = ['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
Если возвращаемое значение итератор, то итерация над ним один раз поглотит его, а последующие итерации приведут к пустому списку:
>>> mock.__iter__.return_value = iter(['a', 'b', 'c'])
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
[]
MagicMock
имеет все поддерживаемые магические методы, за исключением некоторых
скрытых и устаревших. Вы все еще можете настроить их, если хотите.
Волшебные методы, которые поддерживаются, но не устанавливаются по умолчанию в
MagicMock
:
__subclasses__
__dir__
__format__
__get__
,__set__
и__delete__
__reversed__
и__missing__
__reduce__
,__reduce_ex__
,__getinitargs__
,__getnewargs__
,__getstate__
и__setstate__
__getformat__
и__setformat__
[2] | Магические методы следует искать в классе, а не в сущности. Применение этого правила несовместимо с различными версиями Python. Поддерживаемые методы протокола должны работать со всеми поддерживаемыми версиями Python. |
[3] | функция в основном подключена к
классу, но каждый Mock сущность изолирован от других. |
Помощники¶
sentinel¶
-
unittest.mock.
sentinel
¶ Объект
sentinel
обеспечивает удобный способ предоставления уникальных объектов для тестов.Атрибуты создаются по требованию при обращении к ним по имени. При обращении к одному и тому же атрибуту всегда возвращает один и тот же объект. Возвращаемый объекты имеют разумное представление, чтобы сообщения об ошибках тестирования были читаемыми.
Изменено в версии 3.7: Теперь
sentinel
атрибуты сохранять свою идентичность прикопировании
илиpickled
.
Иногда при тестировании необходимо проверить, передан ли конкретный объект в
качестве аргумента другому методу или возвращенный. Для проверки этого может быть
обычным создавать именованные sentinel объекты. sentinel
обеспечивает
удобный способ создания и проверки идентичности подобных объектов.
В этом примере монкей патч (monkey patch) метода
к возвращаемому 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
DEFAULT¶
-
unittest.mock.
DEFAULT
¶ Объект
DEFAULT
является предварительно созданным дозорным (фактическиsentinel.DEFAULT
). Он может быть используем функциямиside_effect
, чтобы указать, что должно быть используемо нормальное возвращаемое значение.
call¶
-
unittest.mock.
call
(*args, **kwargs)¶ call()
является вспомогательным объектом для более простых утверждений, для сравнения сcall_args
,call_args_list
,mock_calls
иmethod_calls
.call()
также можно использовать с помощьюassert_has_calls()
.>>> m = MagicMock(return_value=None) >>> m(1, 2, a='foo', b='bar') >>> m() >>> m.call_args_list == [call(1, 2, a='foo', b='bar'), call()] True
-
call.
call_list
()¶ Для объекта вызова, представляющего несколько вызовов,
call_list()
возвращает список всех промежуточных вызовов, а также окончательный вызов.
call_list
особенно полезно для утверждений в «цепочечных вызовах». Связанный вызов
- это несколько вызовов на одной строке кода. Это приводит к появлению
нескольких записей в mock_calls
на mock. Ручное построение
последовательности вызовов может быть утомительным.
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)¶ Создать mock объект, используя другой объект в качестве spec. Атрибуты на mock будут использовать соответствующие атрибут на объекте spec в качестве своих spec.
Для мока функций или методов будут проверены их аргументы, чтобы убедиться, что они вызваны с правильной сигнатурой.
Если spec_set
True
то попытка задать атрибуты, которые не существуют в объекте spec, вызоветAttributeError
.Если класс используемый как spec, то возвращает значение mock (сущность класса) будет иметь тот же spec. Класс можно использовать в качестве spec для объекта сущность, передавая
instance=True
. Вызов возвращенный mock возможен только в том случае, если сущности mock являются вызываемыми.create_autospec()
также принимает произвольные ключевые аргументы, которые передаются конструктору созданного mock.
Примеры использования автоопределения с Автоспецикация и аргумент create_autospec()
для
autospec см. в разделе patch()
.
Изменено в версии 3.8: create_autospec()
теперь возвращает AsyncMock
, если целевой объект является
асинхронной функцией.
ANY¶
-
unittest.mock.
ANY
¶
Иногда может понадобиться сделать утверждения о some аргументах в вызове
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
- переменная уровня модуля, которая управляет способом реагирования mock
объектов на dir()
(только для Python 2.6 или более поздних). По умолчанию
используется True
, которая использует описанную ниже фильтрацию для
отображения только полезных элементов. Если вам не нравится эта фильтрация или
вам нужно отключить ее в диагностических целях, установите mock.FILTER_DIR = False
.
При включенной фильтрации dir(some_mock)
показывает только полезные атрибуты и
будет включать любые динамически созданные атрибуты, которые обычно не
отображаются. Если 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)¶ Вспомогательная функция для создания mock, заменяющего использование
open()
. Он работает дляopen()
, которые называются напрямую или используемый в качестве менеджера контекста.Аргумент mock является настраиваемым объектом mock. Если
None
(по умолчанию), то для вас будет созданMagicMock
с API, ограниченным методами или атрибутами, доступными на стандартных дескрипторах файлов.read_data - это строка методов
read()
,readline()
иreadlines()
для возвращения дескриптора файла. Вызовы этих методов будут брать данные из read_data до тех пор, пока они не будут исчерпаны. mock этих методов довольно упрощён: каждый раз при вызове mock read_data перематывается на старт. Если вам нужно больше контроля над данными, которые вы подаете в тестируемый код, вам нужно будет настроить этот mock для себя. Если этого недостаточно, один из пакетов файловой системы в памяти на PyPI может предложить реалистичную файловую систему для тестирования.Изменено в версии 3.4: Добавлена поддержка
readline()
иreadlines()
. Вместо того чтобы возвращать mock при каждом вызове,read()
read_data скорее чем возвращать его при каждом вызове.Изменено в версии 3.5: Теперь read_data сбрасывается при каждом вызове mock.
Изменено в версии 3.8: Добавлены
__iter__()
к реализации, чтобы итерация (например, для циклов) правильно потребляла read_data.
Использование open()
в качестве диспетчера контекст - отличный способ
обеспечить правильное закрытие дескрипторов файлов и все чаще используется:
with open('/some/path', 'w') as f:
f.write('something')
Проблема в том, что даже если ваш mock вызов на 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 моков (mocks) api исходного объекта (spec), но он
рекурсивен (реализован лениво), так что атрибуты моков (mocks) имеют только
тот же api, что и атрибуты spec. Кроме того, Mock
функции/методы имеют те же сигнатуры вызова, что и исходные, поэтому они
вызывают TypeError
, если они вызваны неправильно.
Прежде чем я объясню, как работает автоспецикация, вот почему он нужен.
Mock
очень мощный и гибкий объект, но он страдает двумя недостатками,
когда используемый mock объекты из тестируемой системы. Один из этих
недостатков специфичен для 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
для mock используется класс или
сущность, то доступ к атрибуты можно получить только mock,
существующих в реальном классе:
>>> 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'
Это spec относится только к самому mock, поэтому у нас по-прежнему та же проблема с любыми методами на mock:
>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with()
Автоопределение решает эту проблему. Можно либо передать autospec=True
в
patch()
/ patch.object()
, либо использовать функцию create_autospec()
для создания
mock с spec. Если для autospec=True
используется аргумент patch()
,
заменяемый объект будет используемый как объект spec. Поскольку спекуляция
выполняется «лениво» (spec создается по мере обращения к атрибуты на
mock), ее можно использовать с очень сложными или глубоко вложенными
объектами (например, модулями, импортирующими модули, импортирующие модули) без
большого снижения производительности.
Вот пример использования:
>>> 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
есть spec. request.Request
принимает два аргумента в
конструкторе (один из которых - self). Вот что происходит, если мы
пытаемся назвать это неправильно:
>>> req = request.Request()
Traceback (most recent call last):
...
TypeError: <lambda>() takes at least 2 arguments (1 given)
spec также применяется к созданным классам (т.е. возвращает значение спекулированных моков (mocks)):
>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>
Объекты Request
не являются вызываемыми, поэтому возвращает значение
создания экземпляра нашего мок request.Request
не является вызываемым
mock. При наличии spec любые опечатки в наших утверждениях поднимут
правильную ошибку:
>>> 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()
для
создания автоэкспецируемых моков (mocks) непосредственно:
>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>
Однако это не обходится без предостережений и ограничений, поэтому это не поведение по умолчанию. Чтобы узнать, какие атрибуты доступны для объекта spec, autospec должен выполнить интроспекцию (доступ к атрибутам) spec. По мере прохождения атрибуты по mock происходит соответствующее прохождение исходного объекта под капотом. Если какой-либо из указанных объектов имеет свойства или дескрипторы, которые могут инициировать код выполнение, вы можете не использовать 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'
Существует несколько различных способов решения этой проблемы. Самый простой, но не самый неприятный способ - просто установить требуемые атрибуты на mock после создания. Только потому, что autospec не позволяет вам получить атрибуты, которые не существуют на spec, это не мешает вам установить их:
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a = 33
...
Существует более агрессивная версия spec и autospec, которая не предотвращает установку несуществующих атрибутов. Это полезно, если вы хотите, чтобы ваш код sets только действительным атрибуты, но очевидно, что он предотвращает этот конкретный сценарий:
>>> 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
было бы бесполезно как spec, потому что это не позволило
бы вам получить доступ к любым атрибутам или методам на нем. Поскольку
None
никогда будет полезен в качестве spec и, вероятно,
указывает на элемент, который обычно будет иметь какой-либо другой тип, autospec
не использует spec для элементов, для которых задано значение
None
. Это будут просто обычные моки (mocks) (вполне - MagicMocks):
>>> class Something:
... member = None
...
>>> mock = create_autospec(Something)
>>> mock.member.foo.bar.baz()
<MagicMock name='mock.member.foo.bar.baz()' id='...'>
Если изменение производственных классов для добавления значений по умолчанию не
по вашему вкусу, то есть дополнительные параметры. Один из них состоит в том,
чтобы использовать сущность в качестве spec, а не класса. Другим
способом является создание подкласса производственного класса и добавление
значений по умолчанию к подклассу без влияния на производственный класс. Оба
эти параметра требуют использования альтернативного объекта в качестве
spec. К счастью 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] | Это относится только к классам или уже созданным объектам. Вызов
mocked класс, чтобы создать mock сущность не создает
реальную сущность. Выполняется только поиск атрибута - вместе с вызовами
dir() . |
Уплотнительные моки¶
-
unittest.mock.
seal
(mock)¶ Seal отключит автоматическое создание моков (mocks) при доступе к атрибута уплотнительного mock или к любому из его атрибутов, которые уже рекурсивно мокаются.
Если mock сущность с именем или spec назначен атрибут, он не будет учитываться в цепи уплотнения. Это позволяет предотвратить фиксацию уплотнением части mock объекта:
>>> mock = Mock() >>> mock.submock.attribute1 = 2 >>> mock.not_submock = mock.Mock(name="sample_name") >>> seal(mock) >>> mock.new_attribute # Это поднимет AttributeError. >>> mock.submock.attribute2 # Это поднимет AttributeError. >>> mock.not_submock.attribute2 # Это не поднимет.
Добавлено в версии 3.7.