unittest.mock — Библиотека мок (mock) объектов

Добавлено в версии 3.3.


unittest.mock — это библиотека для тестирования на Python. Она позволяет вам заменять части тестируемой системы мок объектами и выполнять утверждения о том, как они использовались.

unittest.mock предоставляет основной класс Mock, избавляющий от необходимости создавать множество заглушек для всего вашего набора тестов. После выполнения действия вы можете утверждать, какие использовались методы/атрибуты и аргументы, с которыми они были вызваны. Вы также можете указать возвращаемые значения и установить необходимые атрибуты обычным способом.

Кроме того, мок предоставляет декоратор patch(), который обрабатывает модули исправлений и атрибуты уровня класса в рамках теста, а также sentinel для создания уникальных объектов. См. Краткий учебник для некоторых примеров использования Mock, MagicMock и patch().

Mock очень прост в использовании и предназначен для использования с unittest. Mock основан на шаблоне «действие -> утверждение», а не на шаблоне «запись -> воспроизведение», используемом во многих мокинг фреймворках.

Существует резервный порт unittest.mock для более ранних версий Python, доступный как mock на PyPI.

Краткий учебник

Объекты Mock и MagicMock создают все атрибуты и методы при доступе к ним и хранят сведения о том, как они использовались. Вы можете настроить их, чтобы указать возвращаемые значения или ограничить доступные атрибуты, а затем сделать утверждения о том, как они использовались:

>>> from unittest.mock import MagicMock
>>> thing = ProductionClass()
>>> thing.method = MagicMock(return_value=3)
>>> thing.method(3, 4, 5, key='value')
3
>>> thing.method.assert_called_with(3, 4, 5, key='value')

side_effect позволяет выполнять побочные эффекты, включая создание исключения при вызове мок:

>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()
Traceback (most recent call last):
 ...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect(arg):
...     return values[arg]
...
>>> mock.side_effect = side_effect
>>> mock('a'), mock('b'), mock('c')
(1, 2, 3)
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)

У Mock есть много других способов его настройки и управления его поведением. Например, аргумент spec настраивает мок на получение своей спецификации от другого объекта. Попытка получить доступ к атрибутам или методам мока, которые не существуют в спецификации, завершится ошибкой с AttributeError.

Декоратор/менеджер контекста patch() упрощает создание классов или мок объектов в тестируемом модуле. Указанный вами объект будет заменён на мок (или другой объект) во время теста и восстановлен по окончании теста:

>>> from unittest.mock import patch
>>> @patch('module.ClassName2')
... @patch('module.ClassName1')
... def test(MockClass1, MockClass2):
...     module.ClassName1()
...     module.ClassName2()
...     assert MockClass1 is module.ClassName1
...     assert MockClass2 is module.ClassName2
...     assert MockClass1.called
...     assert MockClass2.called
...
>>> test()

Примечание

При вложении декораторов патчей мокинги передаются в декорированную функцию в том же порядке, в котором они применялись (обычный порядок Python, в котором применяются декораторы). Это означает снизу вверх, поэтому в приведенном выше примере сначала передаётся мок для module.ClassName1.

Для patch() важно, чтобы вы исправляли объекты в пространстве имён, в котором они просматриваются. Обычно это просто, но для получения краткого руководства прочитайте где патчить.

Помимо декоратора, patch() может использоваться как менеджер контекста в операторе with:

>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
...     thing = ProductionClass()
...     thing.method(1, 2, 3)
...
>>> mock_method.assert_called_once_with(1, 2, 3)

Существует также patch.dict() для установки значений в словаре только во время области и восстановления словаря в исходное состояние по окончании теста:

>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
...     assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original

Mock поддерживает мокинг Python магические методы. Самый простой способ использовать магические методы — использовать класс MagicMock. Это позволяет делать такие вещи, как:

>>> mock = MagicMock()
>>> mock.__str__.return_value = 'foobarbaz'
>>> str(mock)
'foobarbaz'
>>> mock.__str__.assert_called_with()

Mock позволяет вам назначать функции (или другие экземпляры Mock) магическим методам, и они будут вызываться соответствующим образом. Класс MagicMock — это просто вариант Mock, в котором для вас созданы все магические методы (ну, в любом случае, все полезные).

Ниже приведен пример использования магических методов с обычным Mock классом:

>>> mock = Mock()
>>> mock.__str__ = Mock(return_value='wheeeeee')
>>> str(mock)
'wheeeeee'

Чтобы гарантировать, что у мок объектов в ваших тестах тот же API, что и объекты, которые они заменяют, вы можете использовать Автоматическое определение. Автоматическое определение может быть выполнено с помощью аргумента autospec для patch или функции create_autospec(). Автоматическое определение создаёт мок объекты, у которых те же атрибуты и методы, что и заменяемые ими объекты, а у любых функций и методов (включая конструкторы) та же сигнатура вызова, что и реальный объект.

Это гарантирует, что ваши мокинги выйдут из строя так же, как ваш производственный код, если они будут использоваться неправильно:

>>> from unittest.mock import create_autospec
>>> def function(a, b, c):
...     pass
...
>>> mock_function = create_autospec(function, return_value='fishy')
>>> mock_function(1, 2, 3)
'fishy'
>>> mock_function.assert_called_once_with(1, 2, 3)
>>> mock_function('wrong arguments')
Traceback (most recent call last):
 ...
TypeError: <lambda>() takes exactly 3 arguments (1 given)

create_autospec() также можно использовать в классах, где она копирует сигнатуру метода __init__, и на вызываемых объектах, где он копирует сигнатуру метода __call__.

Класс Mock

Mock — это гибкий объект мок, предназначенный для замены использования заглушек и тестовых двойников во всем вашем коде. Моки вызываются и создают атрибуты как новые мокинги, когда вы обращаетесь к ним [1]. Доступ к одному и тому же атрибуту всегда возвращает один и тот же мок. Моки записывают, как вы их используете, позволяя делать утверждения о том, что с ними сделал ваш код.

MagicMock является подклассом Mock со всеми заранее созданными и готовыми к использованию магическими методами. Существуют также невызываемые варианты, полезные, когда вы используете мокинг объекты, которые не могут быть вызваны: NonCallableMock и NonCallableMagicMock

Декораторы patch() упрощают временную замену классов в конкретном модуле объектом Mock. По умолчанию patch() создаст для вас MagicMock. Вы можете указать альтернативный класс Mock, используя аргумент new_callable для patch().

class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)

Создаёт новый объект Mock. Mock принимает несколько необязательных аргументов, определяющих поведение Mock объекта:

  • spec: это может быть списком строк или существующий объект (класс или экземпляр), который действует как спецификация для мок объекта. Если вы передаёте объект, то список строк формируется путём вызова dir для объекта (за исключением неподдерживаемых магических атрибутов и методов). Доступ к любому атрибуту, отсутствующему в этом списке, вызовет AttributeError.

    Если spec является объектом (а не списком строк), то __class__ возвращает класс объекта спецификации. Это позволяет мокингам проходить тесты isinstance().

  • spec_set: более строгий вариант spec. Если используется, попытка set или получение атрибута на мок, которого нет в объекте, переданном как spec_set, вызовет AttributeError.

  • side_effect: функция, вызываемая при каждом вызове Mock. См. атрибут side_effect. Полезно для создания исключений или динамического изменения возвращаемых значений. Функция вызывается с теми же аргументами, что и мок, и, если она не возвращает DEFAULT, возвращаемое значение этой функции используется как возвращаемое значение.

    В качестве альтернативы side_effect может быть классом или экземпляром исключения. В этом случае исключение будет вызвано при вызове мок.

    Если side_effect является итерируемым, то каждый вызов мок будет возвращать следующее значение из итерируемого.

    side_effect можно очистить, установив для него значение None.

  • return_value: значение, возвращаемое при вызове мок. По умолчанию это новый Mock (создаётся при первом доступе). См. атрибут return_value.

  • unsafe: по умолчанию, если какой-либо атрибут начинается с assert или assret, будет повышено значение AttributeError. Передача unsafe=True позволит получить доступ к этим атрибутам.

    Добавлено в версии 3.5.

  • wraps: элемент для мок объекта, который нужно обернуть. Если wraps не None, тогда вызов Mock передаст вызов обернутому объекту (возвращая реальный результат). Доступ к атрибуту на мок вернёт объект Mock, который обертывает соответствующий атрибут обёрнутого объекта (поэтому попытка доступа к несуществующему атрибуту вызовет AttributeError).

    Если у мок есть явная установка return_value, тогда вызовы не передаются в обёрнутый объект, а вместо этого возвращается return_value.

  • name: Если у мок есть имя, то оно будет использоваться в репре для мок. Это может быть полезно для отладки. Имя распространяется на дочерний элемент мокинги.

Моки также можно вызывать с произвольными ключевыми аргументами. Они будут использоваться для установки атрибутов мок после его создания. Подробнее см. метод 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 ложно, тогда вызовы должны быть последовательными. До или после указанных вызовов могут быть дополнительные вызовы.

Если any_order истинно, тогда вызовы могут быть в любом порядке, но все они должны появляться в 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 или любые дочерние атрибуты, которые вы установили с использованием обычного назначения по умолчанию. Если вы хотите сбросить return_value или side_effect, передать соответствующий параметр как True. Дочерний мокинги и возвращаемое значение мок (если есть) также сбрасываются.

Примечание

return_value, и side_effect — только ключевой аргумент.

mock_add_spec(spec, spec_set=False)

Добавить спецификацию к мок. spec может быть объектом или списком строк. Только атрибуты на spec могут быть получены как атрибуты из мок.

Если spec_set истинно, то могут быть установлены только атрибуты, указанные в спецификации.

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(return_value=None)
>>> mock.called
False
>>> mock()
>>> mock.called
True
call_count

Целое число, указывающее, сколько раз был вызван мок объект:

>>> mock = Mock(return_value=None)
>>> mock.call_count
0
>>> mock()
>>> mock()
>>> mock.call_count
2
return_value

Устанавливает это, чтобы настроить значение, возвращаемое при вызове мок:

>>> mock = Mock()
>>> mock.return_value = 'fish'
>>> mock()
'fish'

Возвращаемое значение по умолчанию — объект мок, и вы можете настроить его обычным способом:

>>> mock = Mock()
>>> mock.return_value.attribute = sentinel.Attribute
>>> mock.return_value()
<Mock name='mock()()' id='...'>
>>> mock.return_value.assert_called_with()

return_value также можно задать в конструкторе:

>>> mock = Mock(return_value=3)
>>> mock.return_value
3
>>> mock()
3
side_effect

Это может быть либо функция, вызываемая при вызове мока, итерация, либо исключение (класс или экземпляр), которое должно быть вызвано.

Если вы передадите функцию, она будет вызываться с теми же аргументами, что и мок, и, если функция не вернёт синглтон DEFAULT, вызов мок затем вернёт то, что возвращает функция. Если функция возвращает DEFAULT, то мок вернёт свое нормальное значение (из return_value).

Если вы передаёте итерацию, она используется для получения итератора, который должен возвращать значение при каждом вызове. Это значение может быть либо экземпляром исключения, который должен быть вызван, либо значением, которое должно быть возвращено из вызова мок (обработка DEFAULT идентична случаю функции).

Пример мок, который вызывает исключение (для проверки обработки исключений API):

>>> mock = Mock()
>>> mock.side_effect = Exception('Boom!')
>>> mock()
Traceback (most recent call last):
  ...
Exception: Boom!

Использование side_effect для возврата последовательности значений:

>>> mock = Mock()
>>> mock.side_effect = [3, 2, 1]
>>> mock(), mock(), mock()
(3, 2, 1)

Использование вызываемого:

>>> mock = Mock(return_value=3)
>>> def side_effect(*args, **kwargs):
...     return DEFAULT
...
>>> mock.side_effect = side_effect
>>> mock()
3

side_effect можно задать в конструкторе. Вот пример, который добавляет единицу к значению, с которым вызывается мок, и возвращает его:

>>> side_effect = lambda value: value + 1
>>> mock = Mock(side_effect=side_effect)
>>> mock(3)
4
>>> mock(-8)
-7

Установка side_effect на None очищает его:

>>> m = Mock(side_effect=KeyError, return_value=3)
>>> m()
Traceback (most recent call last):
 ...
KeyError
>>> m.side_effect = None
>>> m()
3
call_args

Либо None (если мок не был вызван), либо аргументы, с которыми последний раз был вызван мок. Он будет в форме кортежа: первый член, к которому также можно получить доступ через свойство args — это любые упорядоченные аргументы, с которыми был вызван мок (или пустой кортеж), а второй член, к которому также можно получить доступ через свойство kwargs — это любые ключевые аргументы (или пустой словарь).

>>> mock = Mock(return_value=None)
>>> print(mock.call_args)
None
>>> mock()
>>> mock.call_args
call()
>>> mock.call_args == ()
True
>>> mock(3, 4)
>>> mock.call_args
call(3, 4)
>>> mock.call_args == ((3, 4),)
True
>>> mock.call_args.args
(3, 4)
>>> mock.call_args.kwargs
{}
>>> mock(3, 4, 5, key='fish', next='w00t!')
>>> mock.call_args
call(3, 4, 5, key='fish', next='w00t!')
>>> mock.call_args.args
(3, 4, 5)
>>> mock.call_args.kwargs
{'key': 'fish', 'next': 'w00t!'}

call_args вместе с членами списков call_args_list, method_calls и mock_calls являются объектами call. Это кортежи, поэтому их можно распаковать, чтобы получить отдельные аргументы и сделать более сложные утверждения. См. вызовы как кортежи.

Изменено в версии 3.8: Добавлены свойства args и kwargs.

call_args_list

Список всех последовательных обращений к объекту мок (таким образом, длина списка равна количеству его вызовов). До того, как были сделаны какие-либо вызовы, это пустой список. Объект call можно использовать для удобного построения списков вызовов для сравнения с call_args_list.

>>> mock = Mock(return_value=None)
>>> mock()
>>> mock(3, 4)
>>> mock(key='fish', next='w00t!')
>>> mock.call_args_list
[call(), call(3, 4), call(key='fish', next='w00t!')]
>>> expected = [(), ((3, 4),), ({'key': 'fish', 'next': 'w00t!'},)]
>>> mock.call_args_list == expected
True

Члены call_args_list — это объекты call. Их можно распаковать как кортежи, чтобы получить отдельные аргументы. См. вызовы как кортежи.

method_calls

Помимо отслеживания вызовов к себе, мокинги также отслеживает вызовы методов и атрибутов, а также их методы и атрибуты:

>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.property.method.attribute()
<Mock name='mock.property.method.attribute()' id='...'>
>>> mock.method_calls
[call.method(), call.property.method.attribute()]

Члены method_calls — это объекты call. Их можно распаковать как кортежи, чтобы получить отдельные аргументы. См. вызовы как кортежи.

mock_calls

mock_calls записывает все вызовы мок объекта, его методы, магические методы и возвращают значение мокингов.

>>> mock = MagicMock()
>>> result = mock(1, 2, 3)
>>> mock.first(a=3)
<MagicMock name='mock.first()' id='...'>
>>> mock.second()
<MagicMock name='mock.second()' id='...'>
>>> int(mock)
1
>>> result(1)
<MagicMock name='mock()()' id='...'>
>>> expected = [call(1, 2, 3), call.first(a=3), call.second(),
... call.__int__(), call()(1)]
>>> mock.mock_calls == expected
True

Члены mock_calls — это объекты call. Их можно распаковать как кортежи, чтобы получить отдельные аргументы. См. вызовы как кортежи.

Примечание

Способ записи mock_calls означает, что там, где выполняются вложенные вызовы, параметры вызовов предков не записываются и поэтому всегда будут сравниваться одинаково:

>>> mock = MagicMock()
>>> mock.top(a=3).bottom()
<MagicMock name='mock.top().bottom()' id='...'>
>>> mock.mock_calls
[call.top(a=3), call.top().bottom()]
>>> mock.mock_calls[-1] == call.top(a=-1).bottom()
True
__class__

Обычно атрибут __class__ объекта возвращает его тип. Для мок объекта с spec __class__ вместо этого возвращает класс спецификации. Позволяет объектам мок проходить тесты isinstance() для объекта, который они заменяют/маскируются:

>>> mock = Mock(spec=3)
>>> isinstance(mock, int)
True

__class__ можно назначить, это позволяет мок пройти проверку isinstance(), не заставляя вас использовать спецификацию:

>>> mock = Mock()
>>> mock.__class__ = dict
>>> isinstance(mock, dict)
True
class unittest.mock.NonCallableMock(spec=None, wraps=None, name=None, spec_set=None, **kwargs)

Невызываемая версия Mock. У параметров конструктора то же значение, что и Mock, за исключением return_value и side_effect, у которых нет значения для невызываемого мок.

Объекты Mock, которые используют класс или экземпляр как spec или spec_set, могут пройти тесты isinstance():

>>> mock = Mock(spec=SomeClass)
>>> isinstance(mock, SomeClass)
True
>>> mock = Mock(spec_set=SomeClass())
>>> isinstance(mock, SomeClass)
True

Классы Mock поддерживают магические мокинг методы. См. магические методы для получения полной информации.

Все классы мок и декораторы patch() принимают произвольные ключевые аргументы для конфигурации. Для декораторов patch() ключевые передаются конструктору создаваемого мок. Ключевые аргументы предназначены для настройки мок атрибутов:

>>> m = MagicMock(attribute=3, other='fish')
>>> m.attribute
3
>>> m.other
'fish'

Возвращаемое значение и побочный эффект дочерних мокингов можно установить таким же образом, используя точечную нотацию. Поскольку вы не можете использовать имена с точками непосредственно в вызове, вам нужно создать словарь и распаковать его с помощью **:

>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
  ...
KeyError

Вызываемый мок, созданный с помощью spec (или 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: Добавлена сигнатурная интроспекция для заданных и автоматически заданных мок объектов.

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)

Асинхронная версия 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 не определён, функция async вернёт значение, определенное return_value, следовательно, по умолчанию функция async возвращает новый объект 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 (если родительский мок — AsyncMock или MagicMock) или Mock (если родительский мок является Mock) Все асинхронные функции будут AsyncMock.

>>> class ExampleClass:
...     def sync_foo():
...         pass
...     async def async_foo():
...         pass
...
>>> a_mock = AsyncMock(ExampleClass)
>>> a_mock.sync_foo
<MagicMock name='mock.sync_foo' id='...'>
>>> a_mock.async_foo
<AsyncMock name='mock.async_foo' id='...'>
>>> mock = Mock(ExampleClass)
>>> mock.sync_foo
<Mock name='mock.sync_foo' id='...'>
>>> mock.async_foo
<AsyncMock name='mock.async_foo' id='...'>

Добавлено в версии 3.8.

assert_awaited()

Утверждать, что мок ждали хоть раз. Обратите внимание, что это происходит отдельно от вызываемого объекта, необходимо использовать ключевой аргумент await:

>>> mock = AsyncMock()
>>> async def main(coroutine_mock):
...     await coroutine_mock
...
>>> coroutine_mock = mock()
>>> mock.called
True
>>> mock.assert_awaited()
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited.
>>> asyncio.run(main(coroutine_mock))
>>> mock.assert_awaited()
assert_awaited_once()

Утверждать, что мок ожидали ровно один раз.

>>> mock = AsyncMock()
>>> async def main():
...     await mock()
...
>>> asyncio.run(main())
>>> mock.assert_awaited_once()
>>> asyncio.run(main())
>>> mock.method.assert_awaited_once()
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited once. Awaited 2 times.
assert_awaited_with(*args, **kwargs)

Утверждать, что последнее ожидание было с указанными аргументами.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_with('foo', bar='bar')
>>> mock.assert_awaited_with('other')
Traceback (most recent call last):
...
AssertionError: expected call not found.
Expected: mock('other')
Actual: mock('foo', bar='bar')
assert_awaited_once_with(*args, **kwargs)

Утверждать, что мок ожидался ровно один раз и с указанными аргументами.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_once_with('foo', bar='bar')
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_once_with('foo', bar='bar')
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited once. Awaited 2 times.
assert_any_await(*args, **kwargs)

Утверждать, что мок когда-либо ожидался с указанными аргументами.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> asyncio.run(main('hello'))
>>> mock.assert_any_await('foo', bar='bar')
>>> mock.assert_any_await('other')
Traceback (most recent call last):
...
AssertionError: mock('other') await not found
assert_has_awaits(calls, any_order=False)

Утверждать, что ожидался мок с указанными вызовами. Список await_args_list проверяется на наличие ожиданий.

Если any_order ложно, то ожидания должны быть последовательными. До или после указанных ожиданий могут быть дополнительные вызовы.

Если any_order истинно, то ожидания могут быть в любом порядке, но все они должны отображаться в await_args_list.

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> calls = [call("foo"), call("bar")]
>>> mock.assert_has_awaits(calls)
Traceback (most recent call last):
...
AssertionError: Awaits not found.
Expected: [call('foo'), call('bar')]
Actual: []
>>> asyncio.run(main('foo'))
>>> asyncio.run(main('bar'))
>>> mock.assert_has_awaits(calls)
assert_not_awaited()

Утверждать, что мок никогда не ждали.

>>> mock = AsyncMock()
>>> mock.assert_not_awaited()
reset_mock(*args, **kwargs)

См. Mock.reset_mock(). Также устанавливает await_count на 0, await_args на None и очищает await_args_list.

await_count

Целое число, отслеживающее, сколько раз объект мок ожидался.

>>> mock = AsyncMock()
>>> async def main():
...     await mock()
...
>>> asyncio.run(main())
>>> mock.await_count
1
>>> asyncio.run(main())
>>> mock.await_count
2
await_args

Это либо None (если мок не ожидалось), либо аргументы, с которыми последний раз ожидался мок. Функционирует так же, как Mock.call_args.

>>> mock = AsyncMock()
>>> async def main(*args):
...     await mock(*args)
...
>>> mock.await_args
>>> asyncio.run(main('foo'))
>>> mock.await_args
call('foo')
>>> asyncio.run(main('bar'))
>>> mock.await_args
call('bar')
await_args_list

Список всех ожиданий, сделанных для мок объекта в последовательности (таким образом, длина списка — это количество ожиданий). До того, как были сделаны какие-либо ожидания, это пустой список.

>>> mock = AsyncMock()
>>> async def main(*args):
...     await mock(*args)
...
>>> mock.await_args_list
[]
>>> asyncio.run(main('foo'))
>>> mock.await_args_list
[call('foo')]
>>> asyncio.run(main('bar'))
>>> mock.await_args_list
[call('foo'), call('bar')]

Вызов

Mock объекты являются вызываемыми. Вызов вернёт значение, установленное как атрибут return_value. Возвращаемое значение по умолчанию — новый объект Mock; он создаётся при первом доступе к возвращаемому значению (явно или путём вызова Mock), но он сохраняется и каждый раз возвращается одно и то же.

Вызовы к объекту будут записаны в таких атрибутах, как call_args и call_args_list.

Если установлено side_effect, то он будет вызываться после записи вызова, поэтому, если side_effect вызывает исключение, вызов все равно записывается.

Самый простой способ заставить мок вызывать исключение при вызове — сделать side_effect классом или экземпляром исключения:

>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
  ...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
  ...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]

Если side_effect — это функция, то все, что возвращает данная функция, — это то, что возвращает мок. Функция side_effect вызывается с теми же аргументами, что и мок. Это позволяет динамически изменять возвращаемое значение вызова в зависимости от ввода:

>>> def side_effect(value):
...     return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]

Если вы хотите, чтобы мок по-прежнему возвращал возвращаемое значение по умолчанию (новый мок) или любое заданное возвращаемое значение, т. е. два способа сделать это. Либо возвращает mock.return_value изнутри side_effect, либо возвращает DEFAULT:

>>> m = MagicMock()
>>> def side_effect(*args, **kwargs):
...     return m.return_value
...
>>> m.side_effect = side_effect
>>> m.return_value = 3
>>> m()
3
>>> def side_effect(*args, **kwargs):
...     return DEFAULT
...
>>> m.side_effect = side_effect
>>> m()
3

Чтобы удалить side_effect и вернуться к поведению по умолчанию, устанавливает side_effect на None:

>>> m = MagicMock(return_value=6)
>>> def side_effect(*args, **kwargs):
...     return 3
...
>>> m.side_effect = side_effect
>>> m()
3
>>> m.side_effect = None
>>> m()
6

side_effect также может быть любым итерируемым объектом. Повторные вызовы мок будут возвращать значения из итерируемого объекта (до тех пор, пока итерируемый объект не будет исчерпан и не возникнет StopIteration):

>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
  ...
StopIteration

Если какие-либо члены итерации являются исключениями, они будут вызваны, а не возвращены:

>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (most recent call last):
 ...
ValueError
>>> m()
66

Удаление атрибутов

Объекты Mock создают атрибуты по запросу. Это позволяет им притворяться объектами любого типа.

Вы можете захотеть, чтобы объект мок возвращал False вызову hasattr() или выдавал AttributeError при выборке атрибута. Вы можете сделать это, указав объект как spec для мок, но это не всегда удобно.

Вы «блокируете» атрибуты, удаляя их. После удаления доступ к атрибуту вызовет AttributeError.

>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
    ...
AttributeError: f

Mock имена и атрибут name

Поскольку «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, когда он ожидает магический метод. Если вам нужна поддержка магического метода, см. магические методы.

Патчеры

Декораторы патчей используются для исправления объектов только в рамках функции, которую они декорируют. Они автоматически обрабатывают распаковку за вас, даже если возникают исключения. Все данные функции также могут использоваться в операторах with или в качестве декораторов классов.

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. Когда оператор function/with завершается, патч отменяется.

Если new пропущен, то цель заменяется на AsyncMock, если исправленный объект является асинхронной функцией, или на MagicMock в противном случае. Если patch() используется в качестве декоратора, а new пропущен, созданный мок передаётся в качестве дополнительного аргумента декорированной функции. Если patch() используется в качестве менеджера контекста, созданный мок возвращается диспетчером контекста.

target должна быть строкой в форме 'package.module.ClassName'. target импортируется, а указанный объект заменяется объектом new, поэтому target необходимо импортировать из среды, из которой вы вызываете patch(). Цель импортируется при выполнении декорированной функции, а не во время декорирования.

Ключевые аргументы spec и spec_set передаются в MagicMock, если patch создаёт его за вас.

Кроме того, вы можете передать spec=True или spec_set=True, что заставит патч передать объект, который является мокингом, как объект spec/spec_set.

new_callable позволяет указать другой класс или вызываемый объект, который будет вызываться для создания объекта new. По умолчанию AsyncMock используется для асинхронных функций, а MagicMock — для остальных.

Более мощная форма specautospec. Если вы устанавливаете autospec=True, то мок будет создан со спецификацией заменяемого объекта. У всех атрибутов мок также будет спецификация соответствующего атрибута заменяемого объекта. У проверяемых методов и функций будут свои аргументы и вызовут TypeError, если они будут вызваны с неправильной сигнатурою. Для мокингов, заменяющего класс, их возвращаемое значение («экземпляр») будет с той же спецификацией, что и класс. См. функцию create_autospec() и Автозапуск.

Вместо autospec=True вы можете передать autospec=some_object, чтобы использовать произвольный объект в качестве спецификации вместо заменяемого.

По умолчанию patch() не сможет заменить несуществующие атрибуты. Если вы передадите create=True, а атрибут не существует, patch создаст для вас атрибут при вызове исправленной функции и удалит его снова после выхода из исправленной функции. Это полезно для написания тестов на атрибуты, которые ваш производственный код создаёт во время выполнения. По умолчанию он отключён, потому что это может быть опасно. Включив его, вы можете писать проходящие тесты для API, которых на самом деле не существует!

Примечание

Изменено в версии 3.5: Если вы исправляете встроенные в модуль, вам не нужно передавать create=True, он будет добавлен по умолчанию.

Патч можно использовать как декоратор класса TestCase. Он работает, украшая каждый тестовый метод в классе. Это уменьшает шаблонный код, когда ваши методы тестирования используют общий множество исправлений. patch() находит тесты, ища имена методов, начинающиеся с patch.TEST_PREFIX. По умолчанию это 'test', что соответствует способу поиска тестов unittest. Вы можете указать альтернативный префикс, задав patch.TEST_PREFIX.

Патч можно использовать как менеджер контекста с помощью оператора with. Здесь исправление применяется к блоку с отступом после оператора with. Если вы используете «as», то исправленный объект будет привязан к имени после «as»; очень полезно, если patch() создаёт для вас объект мок.

patch() принимает произвольные ключевые аргументы. Они будут переданы в Mock (или 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 instance. Если экземпляр класса создаётся в тестируемом коде, то будет использоваться return_value из мок.

Если экземпляр класса создаётся несколько раз, вы можете использовать side_effect, чтобы каждый раз возвращать новый мок. В качестве альтернативы вы можете настроить 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, то возвращаемое значение созданного мок будет с такой же спецификацией.

>>> 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() создаёт для вас мок, обычно первое, что вам нужно сделать, это настроить мок. Некоторые из данных настроек могут быть выполнены при вызове 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'

но добавление create=True в вызов patch() заставит предыдущий пример работать должным образом:

>>> @patch('sys.non_existing_attribute', 42, create=True)
... def test(mock_stdout):
...     assert sys.non_existing_attribute == 42
...
>>> test()

Изменено в версии 3.8: patch() теперь возвращает AsyncMock, если целью является асинхронная функция.

patch.object

patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

Патчит именованный элемент (attribute) на объекте (target) с помощью мок объекта.

patch.object() можно использовать как декоратор, декоратор классов или менеджер контекста. У аргументов new, spec, create, spec_set, autospec и new_callable то же значение, что и для patch(). Как и patch(), patch.object() принимает произвольные ключевые аргументы для настройки создаваемого мок объекта.

При использовании в качестве декоратора класса patch.object() учитывает patch.TEST_PREFIX за выбор методов для переноса.

Вы можете вызвать patch.object() с тремя или двумя аргументами. Форма с тремя аргументами принимает исправляемый объект, имя атрибута и объект, на который нужно заменить атрибут.

При вызове с формой с двумя аргументами вы опускаете замещающий объект, и для вас создаётся мок, который передаётся в качестве дополнительного аргумента декорированной функции:

>>> @patch.object(SomeClass, 'class_method')
... def test(mock_method):
...     SomeClass.class_method(3)
...     mock_method.assert_called_with(3)
...
>>> test()

У spec, create и других аргументов для patch.object() то же значение, что и для patch().

patch.dict

patch.dict(in_dict, values=(), clear=False, **kwargs)

Исправить словарь или объект, подобный словарю, и восстановите словарь в исходное состояние после проверки.

in_dict может быть словарем или отображением, например контейнером. Если это сопоставление, то оно должно как минимум поддерживать получение, установку и удаление элементов, а также перебор ключей.

in_dict также может быть строкой, определяющей имя словаря, которое затем будет извлечено путём его импорта.

values может быть словарем значений для установки в словаре. values также может быть повторением пар (key, value).

Если clear истинно, то словарь будет очищен перед установкой новых значений.

patch.dict() также можно вызвать с произвольными ключевыми аргументами для установки значений в словаре.

Изменено в версии 3.8: patch.dict() теперь возвращает исправленный словарь при использовании в качестве менеджера контекста.

patch.dict() можно использовать как менеджер контекста, декоратор или декоратор класса:

>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
...     assert foo == {'newkey': 'newvalue'}
>>> test()
>>> assert foo == {}

При использовании в качестве декоратора класса patch.dict() учитывает patch.TEST_PREFIX (по умолчанию 'test') для выбора методов для переноса:

>>> import os
>>> import unittest
>>> from unittest.mock import patch
>>> @patch.dict('os.environ', {'newkey': 'newvalue'})
... class TestSample(unittest.TestCase):
...     def test_sample(self):
...         self.assertEqual(os.environ['newkey'], 'newvalue')

Если вы хотите использовать другой префикс для своего теста, вы можете сообщить патчерам другой префикс, установив patch.TEST_PREFIX. Дополнительные сведения об изменении значения см. в TEST_PREFIX.

patch.dict() можно использовать для добавления элементов в словарь или просто позволить тесту изменить словарь и обеспечить восстановление словаря по окончании теста.

>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
...     assert foo == {'newkey': 'newvalue'}
...     assert patched_foo == {'newkey': 'newvalue'}
...     # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
...     patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
...     print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ

Ключевые слова можно использовать в вызове patch.dict() для установки значений в словаре:

>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
...     import mymodule
...     mymodule.function('some', 'args')
...
'fish'

patch.dict() можно использовать с объектами, подобными словарю, которые на самом деле не являются словарями. Как минимум, они должны поддерживать получение, установку, удаление элементов, а также итерацию или проверку членства. Это соответствует магическим методам __getitem__(), __setitem__(), __delitem__() и либо __iter__(), либо __contains__().

>>> class Container:
...     def __init__(self):
...         self.values = {}
...     def __getitem__(self, name):
...         return self.values[name]
...     def __setitem__(self, name, value):
...         self.values[name] = value
...     def __delitem__(self, name):
...         del self.values[name]
...     def __iter__(self):
...         return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
...     assert thing['one'] == 2
...     assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']

patch.multiple

patch.multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

Выполнить несколько патчей за один вызов. Требуется исправляемый объект (либо как объект, либо как строка для извлечения объекта путём импорта) и ключевые аргументы для исправлений:

with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'):
    ...

Используйте DEFAULT в качестве значения, если вы хотите, чтобы patch.multiple() создавал для вас мокинги. В этом случае созданные мокинги передаётся в декорированную функцию по ключевому, а словарь возвращается, когда patch.multiple() используется в качестве менеджера контекста.

patch.multiple() можно использовать как декоратор, декоратор класса или менеджер контекста. У аргументов spec, spec_set, create, autospec и new_callable то же значение, что и для patch(). Данные аргументы будут применены ко всем исправлениям, выполненным patch.multiple().

При использовании в качестве декоратора класса patch.multiple() учитывает patch.TEST_PREFIX за выбор методов для переноса.

Если вы хотите, чтобы patch.multiple() создавал для вас мокинги, вы можете использовать DEFAULT в качестве значения. Если вы используете patch.multiple() в качестве декоратора, то созданный мокинги передаётся в декорированную функцию по ключевому.

>>> thing = object()
>>> other = object()

>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
...     assert isinstance(thing, MagicMock)
...     assert isinstance(other, MagicMock)
...
>>> test_function()

patch.multiple() может быть вложен в другие декораторы patch, но аргументы, переданные ключевым словом after, помещаются в любой из стандартных аргументов, созданных 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(). Это упрощает внесение исправлений в методы 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.

patch встроенных

Вы можете исправить любые встроенные функции в модуле. В следующем примере патчи встроены в 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_functiona.SomeClass. Обе данные формы импорта являются общими. В этом случае класс, который мы хотим исправить, ищется в модуле, поэтому вместо этого мы должны исправить a.SomeClass:

@patch('a.SomeClass')

Исправление дескрипторов и прокси-объектов

И patch, и patch.object правильно исправляют и восстанавливают дескрипторы: методы класса, статические методы и свойства. Вы должны исправить это в классе, а не на экземпляре. Они также работают с некоторыми объектами, у которых есть доступ к атрибутам прокси, например объект настроек django.

MagicMock и поддержка магических методов

Методы мокинг магии

Mock поддерживает мокинг методов протокола Python, также известные как «магические методы». Он позволяет объектам мок заменять контейнеры или другие объекты, реализующие протоколы Python.

Поскольку поиск магических методов отличается от поиска обычных методов [2], эта поддержка была специально реализована. Это означает, что поддерживаются только определенные магические методы. Список поддерживаемых включает почти всех из них. Если вам что-то не хватает, сообщить нам об этом.

Вы можете использовать мок магические методы, задав интересующий вас метод функции или экземпляру мок. Если вы используете функцию, тогда должны принимать self в качестве первого аргумента [3].

>>> def __str__(self):
...     return 'fooble'
...
>>> mock = Mock()
>>> mock.__str__ = __str__
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__str__ = Mock()
>>> mock.__str__.return_value = 'fooble'
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__iter__ = Mock(return_value=iter([]))
>>> list(mock)
[]

Один из вариантов использования для этого — объекты мокинг, используемые в качестве менеджеров контекста в операторе with:

>>> mock = Mock()
>>> mock.__enter__ = Mock(return_value='foo')
>>> mock.__exit__ = Mock(return_value=False)
>>> with mock as m:
...     assert m == 'foo'
...
>>> mock.__enter__.assert_called_with()
>>> mock.__exit__.assert_called_with(None, None, None)

Вызовы магических методов не появляются в method_calls, но они записываются в mock_calls.

Примечание

Если вы используете ключевой аргумент spec для создания мок, то попытка установить волшебный метод, которого нет в спецификации, вызовет AttributeError.

Полный список поддерживаемых магических методов:

  • __hash__, __sizeof__, __repr__ и __str__
  • __dir__, __format__ и __subclasses__
  • __round__, __floor__, __trunc__ и __ceil__
  • Сравнения: __lt__, __gt__, __le__, __ge__, __eq__ и __ne__
  • Методы контейнера: __getitem__, __setitem__, __delitem__, __contains__, __len__, __iter__, __reversed__ и __missing__
  • Менеджер контекста: __enter__, __exit__, __aenter__ и __aexit__
  • Унарные числовые методы: __neg__, __pos__ и __invert__
  • Числовые методы (включая варианты для правой руки и на месте): __add__, __sub__, __mul__, __matmul__, __div__, __truediv__, __floordiv__, __mod__, __divmod__, __lshift__, __rshift__, __and__, __xor__, __or__, и __pow__
  • Числовые методы преобразования: __complex__, __int__, __float__ и __index__
  • Дескрипторные методы: __get__, __set__ и __delete__
  • Травление: __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate__ и __setstate__
  • Представление пути в файловой системе: __fspath__
  • Асинхронные итерационные методы: __aiter__ и __anext__

Изменено в версии 3.8: Добавлена поддержка os.PathLike.__fspath__().

Изменено в версии 3.8: Добавлена поддержка __aenter__, __aexit__, __aiter__ и __anext__.

Существуют следующие методы, но не поддерживаются, поскольку они либо используются мок, не могут быть установлены динамически или могут вызвать проблемы:

  • __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, будут созданы только магические методы, существующие в спецификации.

class unittest.mock.NonCallableMagicMock(*args, **kw)

Не вызываемая версия MagicMock.

У параметров конструктора то же значение, что и для MagicMock, за исключением return_value и side_effect, у которых нет значения для невызываемого мока.

Магические методы настраиваются с помощью объектов MagicMock, поэтому вы можете настроить их и использовать обычным образом:

>>> mock = MagicMock()
>>> mock[3] = 'fish'
>>> mock.__setitem__.assert_called_with(3, 'fish')
>>> mock.__getitem__.return_value = 'result'
>>> mock[2]
'result'

По умолчанию многие методы протокола требуются для возврата объектов определенного типа. Данные методы предварительно сконфигурированы с возвращаемым значением по умолчанию, поэтому их можно использовать без каких- либо действий, если вы не заинтересованы в возвращаемом значении. Вы все ещё можете установить возвращаемое значение вручную, если хотите изменить значение по умолчанию.

Методы и их значения по умолчанию:

  • __lt__: NotImplemented
  • __gt__: NotImplemented
  • __le__: NotImplemented
  • __ge__: NotImplemented
  • __int__: 1
  • __contains__: False
  • __len__: 0
  • __iter__: iter([])
  • __exit__: False
  • __aexit__: False
  • __complex__: 1j
  • __float__: 1.0
  • __bool__: True
  • __index__: 1
  • __hash__: хеш по умолчанию для мок
  • __str__: стандартная строка для мок
  • __sizeof__: размер по умолчанию для мок

Например:

>>> mock = MagicMock()
>>> int(mock)
1
>>> len(mock)
0
>>> list(mock)
[]
>>> object() in mock
False

Два метода равенства, __eq__() и __ne__(), являются особенными. Они выполняют сравнение идентичности по умолчанию с использованием атрибута side_effect, если вы не изменить их возвращаемое значение, чтобы вернуть что-то ещё:

>>> MagicMock() == 3
False
>>> MagicMock() != 3
True
>>> mock = MagicMock()
>>> mock.__eq__.return_value = True
>>> mock == 3
True

Возвращаемое значение MagicMock.__iter__() может быть любым итеративным объектом, и не обязательно быть итератором:

>>> mock = MagicMock()
>>> mock.__iter__.return_value = ['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']

Если возвращаемое значение представляет собой итератор, то при однократной итерации он будет использован, а последующие итерации приведут к пустому списку:

>>> mock.__iter__.return_value = iter(['a', 'b', 'c'])
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
[]

В MagicMock настроены все поддерживаемые магические методы, за исключением некоторых малоизвестных и устаревших. Вы все ещё можете настроить их, если хотите.

Магические методы, которые поддерживаются, но не настроены по умолчанию в MagicMock,следующие:

  • __subclasses__
  • __dir__
  • __format__
  • __get__, __set__ и __delete__
  • __reversed__ и __missing__
  • __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate__ и __setstate__
  • __getformat__ и __setformat__
[2]Магические методы следует искать в классе, а не в экземпляре. Различные версии Python несовместимы в применении этого правило. Поддерживаемые методы протокола должны работать со всеми поддерживаемыми версиями Python.
[3]Функция в основном привязана к классу, но каждый Mock экземпляр хранится изолированно от других.

Помощники

sentinel

unittest.mock.sentinel

Объект sentinel предоставляет удобный способ предоставления уникальных объектов для ваших тестов.

Атрибуты создаются по запросу, когда вы обращаетесь к ним по имени. Доступ к одному и тому же атрибуту всегда возвращает один и тот же объект. У возвращённых объектов есть разумное воспроизведение, так что сообщения об ошибках теста читаются.

Изменено в версии 3.7: Атрибуты sentinel теперь сохраняют свою идентичность, когда они являются copied или pickled.

Иногда при тестировании необходимо проверить, что конкретный объект передаётся в качестве аргумента другому методу или возвращается. Для проверки этого может быть обычным делом создание именованных дозорных объектов. 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
>>> 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 на мок. Построение последовательности вызовов вручную может быть утомительным.

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) в зависимости от того, как он был построен. Когда вы создаёте их самостоятельно, это не особенно интересно, но объекты call, которые находятся в атрибутах Mock.call_args, Mock.call_args_list и Mock.mock_calls, могут быть подвергнуты интроспекции, чтобы получить отдельные аргументы, которые они содержат.

Объекты call в Mock.call_args и Mock.call_args_list являются двумя кортежами (позиционные аргументы, ключевые аргументы), тогда как объекты call в Mock.mock_calls вместе с теми, которые вы создаёте сами, представляют собой трехкортежи (имя, позиционные аргументы, ключевые аргументы).

Вы можете использовать их «кортеж», чтобы извлечь отдельные аргументы для более сложного самоанализа и утверждений. Позиционные аргументы — это кортеж (пустой кортеж, если нет позиционных аргументов), а ключевые аргументы — это словарь:

>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> kall.args
(1, 2, 3)
>>> kall.kwargs
{'arg': 'one', 'arg2': 'two'}
>>> kall.args is kall[0]
True
>>> kall.kwargs is kall[1]
True
>>> m = MagicMock()
>>> m.foo(4, 5, 6, arg='two', arg2='three')
<MagicMock name='mock.foo()' id='...'>
>>> kall = m.mock_calls[0]
>>> name, args, kwargs = kall
>>> name
'foo'
>>> args
(4, 5, 6)
>>> kwargs
{'arg': 'two', 'arg2': 'three'}
>>> name is m.mock_calls[0][0]
True

create_autospec

unittest.mock.create_autospec(spec, spec_set=False, instance=False, **kwargs)

Создать объект мок, используя другой объект в качестве спецификации. Атрибуты на мок будут использовать соответствующий атрибут на объекте spec в качестве своей спецификации.

Проверяемые функции или методы будут проверять аргументы, чтобы гарантировать, что они вызываются с правильной сигнатурою.

Если spec_set — это True, то попытка установить атрибуты, которые не существуют в объекте спецификации, вызовет AttributeError.

Если класс используется в качестве спецификации, то возвращаемое значение мок (экземпляр класса) будет с такой же спецификацией. Вы можете использовать класс в качестве спецификации для объекта-экземпляра, передав instance=True. Возвращенный мок будет доступен только в том случае, если экземпляры мок являются вызываемыми.

create_autospec() также принимает произвольные ключевые аргументы, которые передаются конструктору созданного мок.

См. в Автозапуск примеры того, как использовать автоматическое определение с create_autospec() и аргумент autospec для patch().

Изменено в версии 3.8: create_autospec() теперь возвращает AsyncMock, если целью является асинхронная функция.

ANY

unittest.mock.ANY

Иногда вам может потребоваться сделать утверждения о некоторых аргументах при вызове мок, но либо не заботиться о некоторых из аргументов, либо вы хотите извлечь их по отдельности из call_args и сделать по ним более сложные утверждения.

Чтобы игнорировать определенные аргументы, вы можете передать объекты, которые сравниваются как все. Вызовы assert_called_with() и assert_called_once_with() будут успешными независимо от того, что было передано.

>>> mock = Mock(return_value=None)
>>> mock('foo', bar=object())
>>> mock.assert_called_once_with('foo', bar=ANY)

ANY также можно использовать для сравнения со списками вызовов, такими как mock_calls:

>>> m = MagicMock(return_value=None)
>>> m(1)
>>> m(1, 2)
>>> m(object())
>>> m.mock_calls == [call(1), call(1, 2), ANY]
True

FILTER_DIR

unittest.mock.FILTER_DIR

FILTER_DIR — это переменная уровня модуля, которая контролирует способ ответа мок объектов на dir() (только для Python 2.6 или более поздних версий). По умолчанию используется True, который использует фильтрацию, описанную ниже, только для отображения полезных элементов. Если вам не нравится эта фильтрация или вам необходимо отключить её для диагностических целей, устанавливает mock.FILTER_DIR = False.

При включенной фильтрации dir(some_mock) показывает только полезные атрибуты и будет включать любые динамически созданные атрибуты, которые обычно не отображаются. Если мок был создан с spec (или, конечно, autospec), то отображаются все атрибуты из оригинала, даже если к ним ещё не обращались:

>>> dir(Mock())
['assert_any_call',
 'assert_called',
 'assert_called_once',
 'assert_called_once_with',
 'assert_called_with',
 'assert_has_calls',
 'assert_not_called',
 'attach_mock',
 ...
>>> from urllib import request
>>> dir(Mock(spec=request))
['AbstractBasicAuthHandler',
 'AbstractDigestAuthHandler',
 'AbstractHTTPHandler',
 'BaseHandler',
 ...

Многие из не очень полезных (частные для Mock, а не для мокинга) атрибуты подчеркивания и двойного подчеркивания с префиксом были отфильтрованы из результата вызова dir() на Mock. Если вам не нравится такое поведение, вы можете отключить его, установив переключатель уровня модуля FILTER_DIR:

>>> from unittest import mock
>>> mock.FILTER_DIR = False
>>> dir(mock.Mock())
['_NonCallableMock__get_return_value',
 '_NonCallableMock__get_side_effect',
 '_NonCallableMock__return_value_doc',
 '_NonCallableMock__set_return_value',
 '_NonCallableMock__set_side_effect',
 '__call__',
 '__class__',
 ...

В качестве альтернативы вы можете просто использовать vars(my_mock) (элементы экземпляра) и dir(type(my_mock)) (элементы типа), чтобы обойти фильтрацию независимо от mock.FILTER_DIR.

mock_open

unittest.mock.mock_open(mock=None, read_data=None)

Вспомогательная функция для создания мок вместо использования open(). Он работает для open(), вызываемого напрямую или используемого в качестве менеджера контекста.

Аргумент mock — это настраиваемый объект мок. Если None (по умолчанию), то для вас будет создан MagicMock, при этом API будет ограничен методами или атрибутами, доступными для стандартных дескрипторов файлов.

read_data — это строка для методов read(), readline() и readlines() возвращаемого дескриптора файла. Вызов данных методов будет принимать данные из read_data до тех пор, пока они не будут исчерпаны. Мок данных методов довольно упрощён: каждый раз, когда вызывается mock, read_data перематывается в начало. Если вам нужен больший контроль над данными, которые вы передаете в проверенный код, вам нужно будет настроить данный мок под себя. Когда этого недостаточно, один из пакетов файловой системы в памяти на 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 мок. Он ограничивает api мокингов до api исходного объекта (спецификации), но он рекурсивен (реализован лениво), так что у атрибутов мокинга есть только тот же api, что и атрибуты спецификации. Кроме того, у мок функций/методов та же сигнатура вызова, что и исходные, поэтому они вызывают TypeError, если они вызываются неправильно.

Прежде чем я объясню, как работает автоматическая спецификация, вот почему это необходимо.

Mock — очень мощный и гибкий объект, но он страдает двумя недостатками при использовании мок для извлечения объектов из тестируемой системы. Один из данных недостатков характерен для API Mock, а другой — более общая проблема с использованием мок объектов.

Сначала проблема, специфичная для Mock. У Mock есть два чрезвычайно удобных метода assert: assert_called_with() и assert_called_once_with().

>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
Traceback (most recent call last):
 ...
AssertionError: Expected 'mock' to be called once. Called 2 times.

Поскольку мокинги автоматически создают атрибуты по запросу и позволяет вам вызывать их с произвольными аргументами, если вы ошибетесь в написании одного из данных методов assert, ваше утверждение исчезнет:

>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assret_called_once_with(4, 5, 6)

Из-за опечатки ваши тесты могут проходить тихо и некорректно.

Вторая проблема более общая для мокингов. Если вы реорганизуете часть своего кода, переименуете элементы и т. д., любые тесты для кода, который все ещё использует старое api, но использует мокинги вместо реальных объектов, все равно пройдут. Это означает, что все ваши тесты могут пройти, даже если ваш код сломан.

Обратите внимание, что это ещё одна причина, по которой вам нужны интеграционные тесты, а также модульные тесты. Тестировать все изолированно — это нормально и изящно, но если вы не протестируете, как ваши устройства «соединены вместе», остается много места для ошибок, которые могли быть обнаружены тестами.

mock уже предоставляет функцию, помогающую с этим, называемую спецификацией. Если вы используете класс или экземпляр как spec для мок, тогда вы можете получить доступ только к атрибутам на мок, которые существуют в реальном классе:

>>> from urllib import request
>>> mock = Mock(spec=request.Request)
>>> mock.assret_called_with
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'assret_called_with'

Спецификация применима только к самому мок, поэтому у нас все ещё есть та же проблема с любыми методами на мок:

>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with()

Автоматическое определение решает эту проблему. Вы можете передать autospec=True в patch() / patch.object() или использовать функцию create_autospec() для создания мок со спецификацией. Если вы используете аргумент autospec=True для patch(), то заменяемый объект будет использоваться как объект спецификации. Поскольку спецификация выполняется «лениво» (спецификация создаётся при доступе к атрибутам на мок), вы можете использовать её с очень сложными или глубоко вложенными объектами (например, с модулями, которые импортируют модули, которые импортируют модули) без большого снижения производительности.

Вот пример его использования:

>>> from urllib import request
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> request is mock_request
True
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>

Вы можете видеть, что у request.Request есть спецификация. request.Request принимает в конструкторе два аргумента (один из которых — self). Вот что произойдет, если мы попытаемся вызвать это неправильно:

>>> req = request.Request()
Traceback (most recent call last):
 ...
TypeError: <lambda>() takes at least 2 arguments (1 given)

Спецификация также применима к экземплярам классов (т. е. возвращаемому значению заданных мокингов):

>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>

Объекты Request не могут быть вызваны, поэтому возвращаемое значение при создании экземпляра нашего выдуманного request.Request — это не вызываемое мок. При наличии спецификации любые опечатки в наших утверждениях приведут к правильной ошибке:

>>> req.add_header('spam', 'eggs')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_with
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')

Во многих случаях вы сможете просто добавить autospec=True к своим существующим вызовам patch() и тогда будете защищены от ошибок из-за опечаток и изменений API.

Помимо использования с autospec по patch(), существует create_autospec() для непосредственного создания автоматически заданного мокингами:

>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>

Однако здесь есть предостережения и ограничения, поэтому это не поведение по умолчанию. Чтобы узнать, какие атрибуты доступны в объекте спецификации, autospec должен проанализировать (получить доступ к атрибутам) спецификацию. При обходе атрибутов на мок соответствующий обход исходного объекта происходит под капотом. Если у какого-либо из ваших заданных объектов есть свойства или дескрипторы, которые могут запускать выполнение кода, вы не сможете использовать autospec. С другой стороны, гораздо лучше проектировать объекты таким образом, чтобы интроспекция была безопасной [4].

Более серьезная проблема заключается в том, что обычно атрибуты экземпляра создаются в методе __init__() и не существуют в классе вообще. autospec не может знать ни о каких динамически создаваемых атрибутах и ограничивает api видимыми атрибутами.

>>> class Something:
...   def __init__(self):
...     self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
...   thing = Something()
...   thing.a
...
Traceback (most recent call last):
  ...
AttributeError: Mock object has no attribute 'a'

Есть несколько способов решить эту проблему. Самый простой, но не обязательно наименее раздражающий способ — просто установить необходимые атрибуты на мок после создания. Тот факт, что autospec не позволяет вам получать атрибуты, которых нет в спецификации, не мешает вам их устанавливать:

>>> with patch('__main__.Something', autospec=True):
...   thing = Something()
...   thing.a = 33
...

Существует более агрессивная версия spec и autospec, которая не позволяет вам устанавливать несуществующие атрибуты. Это полезно, если вы хотите, чтобы в вашем коде также были допустимые установленные атрибуты, но, очевидно, это предотвращает данный конкретный сценарий:

>>> with patch('__main__.Something', autospec=True, spec_set=True):
...   thing = Something()
...   thing.a = 33
...
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'a'

Вероятно, лучший способ решения проблемы — добавить атрибуты класса в качестве значений по умолчанию для членов экземпляра, инициализированных в __init__(). Обратите внимание, что если вы устанавливаете только атрибуты по умолчанию в __init__(), то предоставление их через атрибуты класса (разумеется, совместно используемые между экземплярами) тоже будет быстрее, например.

class Something:
    a = 33

Это вызывает другую проблему. Относительно часто предоставляется значение по умолчанию None для элементов, которые позже станут объектом другого типа. None будет бесполезным в качестве спецификации, потому что он не позволит вам получить доступ к каким-либо атрибутам или методам на нём. Поскольку None никогда не будет полезен в качестве спецификации и, вероятно, указывает на элемент, который обычно относится к другому типу, autospec не использует спецификацию для членов, для которых установлено значение None. Это будет просто обычный мокинги (ну — MagicMocks):

>>> class Something:
...     member = None
...
>>> mock = create_autospec(Something)
>>> mock.member.foo.bar.baz()
<MagicMock name='mock.member.foo.bar.baz()' id='...'>

Если изменение производственных классов для добавления значений по умолчанию вам не по душе, есть другие варианты. Один из них — просто использовать экземпляр в качестве спецификации, а не класса. Другой — создать подкласс производственного класса и добавить в подкласс значения по умолчанию, не затрагивая производственный класс. Оба они требуют, чтобы вы использовали альтернативный объект в качестве спецификации. К счастью, patch() поддерживает это — вы можете просто передать альтернативный объект в качестве аргумента autospec:

>>> class Something:
...   def __init__(self):
...     self.a = 33
...
>>> class SomethingForTest(Something):
...   a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>
[4]Это применимо только к классам или уже созданным объектам. Вызов фиктивный класс для создания экземпляра мок не создаёт реальный экземпляр. Выполняется только поиск атрибутов и вызовы dir().

Уплотнение мокингов

unittest.mock.seal(mock)

Seal отключит автоматическое создание мокингов при рекурсивном доступе к атрибуту запечатываемого мок или любому из его атрибутов, которые уже являются мокингами.

Если для атрибута назначен мок экземпляру с именем или спецификацией, он не будет учитываться в цепочке запечатывания (sealing). Это позволяет предотвратить фиксацию уплотнением части мок объекта.

>>> 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.