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

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()

Изменено в версии 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) с помощью 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.