unittest.mock — библиотека макетных объектов

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

Исходный код: Lib/unittest/mock.py


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

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

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

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

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

Краткое руководство

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

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

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

>>> mock = 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 на получение спецификации из другого объекта. Попытка получить доступ к атрибутам или методам в макете, которых нет в спецификации, завершится ошибкой с AttributeError.

Контекстный менеджер patch() decorator позволяет легко имитировать классы или объекты в тестируемом модуле. Указанный вами объект будет заменен на макет (или другой объект) во время тестирования и восстановлен после завершения теста:

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

Примечание

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

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

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

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

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

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

Mock поддерживает имитацию Python magic methods. Проще всего использовать магические методы с помощью класса MagicMock. Он позволяет выполнять такие действия, как:

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

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

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

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

Чтобы убедиться, что фиктивные объекты в ваших тестах имеют тот же api, что и объекты, которые они заменяют, вы можете использовать auto-speccing. Автоматическое определение может быть выполнено с помощью аргумента 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 - это гибкий макетный объект, предназначенный для замены использования заглушек и тестовых двойников в вашем коде. Макеты могут вызываться и создавать атрибуты в качестве новых макетов при обращении к ним [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 принимает несколько необязательных аргументов, которые определяют поведение фиктивного объекта:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Утверждение проходит, если 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_calls проверен на наличие вызовов.

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

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

>>> mock = Mock(return_value=None)
>>> mock(1)
>>> mock(2)
>>> mock(3)
>>> mock(4)
>>> calls = [call(2), call(3)]
>>> mock.assert_has_calls(calls)
>>> calls = [call(4), call(2), call(3)]
>>> mock.assert_has_calls(calls, any_order=True)
assert_not_called()

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

>>> m = Mock()
>>> m.hello.assert_not_called()
>>> obj = m.hello()
>>> m.hello.assert_not_called()
Traceback (most recent call last):
  ...
AssertionError: Expected 'hello' to not have been called. Called 1 times.

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

reset_mock(*, return_value=False, side_effect=False)

Метод reset_mock сбрасывает все атрибуты вызова для фиктивного объекта:

>>> mock = Mock(return_value=None)
>>> mock('hello')
>>> mock.called
True
>>> mock.reset_mock()
>>> mock.called
False

Изменено в версии 3.6: Добавлены два аргумента только для ключевых слов в функцию reset_mock.

Это может быть полезно, если вы хотите создать серию утверждений, которые повторно используют один и тот же объект. Обратите внимание, что reset_mock() не очищает возвращаемое значение, :attr:`side_effect` или любые дочерние атрибуты, которые вы установили с помощью обычного присваивания по умолчанию. Если вы хотите сбросить значение *return_value или side_effect, то передайте соответствующий параметр как True. Дочерние макеты и макет возвращаемого значения (если таковые имеются) также сбрасываются.

Примечание

return_value и side_effect являются аргументами только для ключевых слов.

mock_add_spec(spec, spec_set=False)

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

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

attach_mock(mock, attribute)

Прикрепите макет в качестве атрибута к этому макету, заменив его имя и родительский элемент. Обращения к прикрепленному макету будут записаны в атрибутах method_calls и mock_calls этого макета.

configure_mock(**kwargs)

Задайте атрибуты в макете с помощью аргументов ключевого слова.

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

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

То же самое может быть достигнуто при вызове конструктора для mocks:

>>> 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) полезными результатами. Для макетов с спецификацией это включает все разрешенные атрибуты для макета.

Смотрите 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

call_args_list

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

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

Элементы call_args_list являются объектами call. Их можно распаковать в виде кортежей, чтобы получить доступ к отдельным аргументам. Смотрите calls as tuples.

method_calls

Помимо отслеживания обращений к самим себе, 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. Их можно распаковать в виде кортежей, чтобы получить доступ к отдельным аргументам. Смотрите calls as tuples.

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. Их можно распаковать в виде кортежей, чтобы получить доступ к отдельным аргументам. Смотрите calls as tuples.

Примечание

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

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

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

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

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

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

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

Имитационные объекты, использующие класс или экземпляр в качестве spec или spec_set, могут проходить isinstance() тесты:

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

Классы Mock поддерживают имитацию магических методов. Более подробную информацию смотрите в magic methods.

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

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

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

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

Вызываемый макет, созданный с помощью spec (или spec_set), будет анализировать сигнатуру объекта спецификации при сопоставлении вызовов с макетом. Таким образом, он может соответствовать фактическим аргументам вызова независимо от того, были ли они переданы позиционно или по имени:

>>> def f(a, b, c): pass
...
>>> mock = Mock(spec=f)
>>> mock(1, 2, c=3)
<Mock name='mock()' id='140161580456576'>
>>> mock.assert_called_with(1, 2, 3)
>>> mock.assert_called_with(a=1, b=2, c=3)

Это относится к assert_called_with(), assert_called_once_with(), assert_has_calls() и assert_any_call(). Когда Автоматическая настройка, это также будет применяться к вызовам методов для фиктивного объекта.

Изменено в версии 3.4: Добавлен самоанализ подписи для специальных и автоматически настраиваемых макетных объектов.

class unittest.mock.PropertyMock(*args, **kwargs)

Макет, предназначенный для использования в качестве property или другого descriptor в классе. 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)]

Из-за способа хранения фиктивных атрибутов вы не можете напрямую прикрепить PropertyMock к фиктивному объекту. Вместо этого вы можете прикрепить его к объекту фиктивного типа:

>>> m = MagicMock()
>>> p = PropertyMock(return_value=3)
>>> type(m).foo = p
>>> m.foo
3
>>> p.assert_called_once_with()
class unittest.mock.AsyncMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)

Асинхронная версия MagicMock. Объект AsyncMock будет вести себя таким образом, что объект распознается как асинхронная функция, а результатом вызова является ожидаемая функция.

>>> mock = AsyncMock()
>>> asyncio.iscoroutinefunction(mock)
True
>>> inspect.isawaitable(mock())  
True

Результатом mock() является асинхронная функция, которая будет иметь результат side_effect или return_value после того, как он будет ожидаться:

  • если side_effect является функцией, то асинхронная функция вернет результат этой функции,

  • если side_effect является исключением, асинхронная функция вызовет это исключение,

  • если side_effect является итерируемым, асинхронная функция вернет следующее значение итерируемого значения, однако, если последовательность результатов исчерпана, StopAsyncIteration вызывается немедленно,

  • если side_effect не определено, асинхронная функция вернет значение, определенное с помощью return_value, следовательно, по умолчанию асинхронная функция возвращает новый объект AsyncMock.

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

>>> async def async_func(): pass
...
>>> mock = MagicMock(async_func)
>>> mock
<MagicMock spec='function' id='...'>
>>> mock()  
<coroutine object AsyncMockMixin._mock_call at ...>

Установка spec для класса с асинхронными и синхронными функциями Mock, MagicMock, или AsyncMock автоматически определит синхронные функции и установит для них значение MagicMock (если родительский класс имеет асинхронные и синхронные функции). макет равен 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 равно false, то ожидания должны быть последовательными. Могут быть дополнительные вызовы до или после указанного ожидания.

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

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

Утверждаю, что розыгрыша никто и не ждал.

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

Смотрите Mock.reset_mock(). Также для await_count устанавливается значение 0, для await_args - значение Нет, а для 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')]

Зовущий

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

Вызовы, выполненные к объекту, будут записаны в таких атрибутах, как 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 является функцией, то все, что возвращает эта функция, является вызовом функции mock return. Функция 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.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 будут возвращать значения из iterable (до тех пор, пока не будет исчерпан iterable и не будет поднят StopIteration).:

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

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

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

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

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

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

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

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

Фиктивные имена и атрибут name

Поскольку «name» является аргументом конструктора Mock, если вы хотите, чтобы ваш макетный объект имел атрибут «name», вы не можете просто передать его во время создания. Есть две альтернативы. Один из вариантов - использовать configure_mock():

>>> mock = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'my_name'

Более простой вариант - просто установить атрибут «name» после создания макета:

>>> mock = MagicMock()
>>> mock.name = "foo"

Прикрепление макетов в качестве атрибутов

Когда вы добавляете mock в качестве атрибута другого mock (или в качестве возвращаемого значения), он становится «дочерним» по отношению к этому mock. Обращения к дочернему элементу записываются в атрибутах 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')]

Исправители

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

заплатка

Примечание

Главное - выполнить исправление в нужном пространстве имен. Смотрите раздел where to patch.

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

patch() действует как декоратор функции, декоратор класса или контекстный менеджер. Внутри тела функции или инструкции with целевой объект исправляется с помощью нового объекта. Когда функция с инструкцией завершает работу, исправление отменяется.

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

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

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

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

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

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

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

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

Примечание

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

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

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

patch() принимает произвольные аргументы ключевого слова. Они будут переданы в AsyncMock, если исправленный объект является асинхронным, в MagicMock в противном случае или в new_callable, если указано.

patch.dict(...), patch.multiple(...) и patch.object(...) доступны для альтернативных вариантов использования.

patch() как декоратор функции, создающий для вас макет и передающий его в оформляемую функцию:

>>> @patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
...     print(mock_class is SomeClass)
...
>>> function(None)
True

При исправлении класса класс заменяется экземпляром MagicMock *. Если в тестируемом коде создан экземпляр класса, то будет использоваться return_value из макета.

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

Чтобы настроить возвращаемые значения для методов экземпляров исправленного класса, вы должны сделать это в return_value. Например:

>>> class Class:
...     def method(self):
...         pass
...
>>> with patch('__main__.Class') as MockClass:
...     instance = MockClass.return_value
...     instance.method.return_value = 'foo'
...     assert Class() is instance
...     assert Class().method() == 'foo'
...

Если вы используете spec или spec_set, а patch() заменяет 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_attribute'

но добавление 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(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

вставьте именованный элемент (атрибут) в объект (цель) с помощью фиктивного объекта.

patch.object() может использоваться в качестве декоратора, декоратора классов или контекстного менеджера. Аргументы new, spec, create, spec_set, autospec и new_callable имеют то же значение, что и для patch(). Like 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.диктовать

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

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

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

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

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

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

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

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

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

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

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

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

Если вы хотите использовать другой префикс для вашего теста, вы можете сообщить разработчикам исправлений о другом префиксе, установив patch.TEST_PREFIX. Более подробную информацию о том, как изменить значение параметра, смотрите в разделе ТЕСТ_ПРЕФИКС.

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(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() в качестве декоратора, то созданные макеты передаются в функцию decorated по ключевому слову.

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

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

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

>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
...     assert 'other' in repr(other)
...     assert 'thing' in repr(thing)
...     assert 'exit' in repr(mock_exit)
...
>>> test_function()

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

>>> with patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) as values:
...     assert 'other' in repr(values['other'])
...     assert 'thing' in repr(values['thing'])
...     assert values['thing'] is thing
...     assert values['other'] is other
...

методы исправления: запуск и остановка

Все исправления имеют методы start() и stop(). Это упрощает выполнение исправлений с помощью методов setUp или там, где вы хотите выполнить несколько исправлений без вложенных декораторов или с помощью инструкций.

Чтобы использовать их, вызовите patch(), patch.object() или patch.dict() как обычно и сохраните ссылку на возвращаемый объект patcher. Затем вы можете вызвать start(), чтобы установить исправление на место, и stop(), чтобы отменить его.

Если вы используете patch() для создания макета для себя, то он будет возвращен вызовом patcher.start.

>>> patcher = patch('package.module.ClassName')
>>> from package import module
>>> original = module.ClassName
>>> new_mock = patcher.start()
>>> assert module.ClassName is not original
>>> assert module.ClassName is new_mock
>>> patcher.stop()
>>> assert module.ClassName is original
>>> assert module.ClassName is not new_mock

Типичным вариантом использования этого может быть выполнение нескольких исправлений в методе setUp из TestCase:

>>> class MyTest(unittest.TestCase):
...     def setUp(self):
...         self.patcher1 = patch('package.module.Class1')
...         self.patcher2 = patch('package.module.Class2')
...         self.MockClass1 = self.patcher1.start()
...         self.MockClass2 = self.patcher2.start()
...
...     def tearDown(self):
...         self.patcher1.stop()
...         self.patcher2.stop()
...
...     def test_something(self):
...         assert package.module.Class1 is self.MockClass1
...         assert package.module.Class2 is self.MockClass2
...
>>> MyTest('test_something').run()

Осторожно

Если вы используете этот метод, вы должны убедиться, что исправление «отменено», вызвав stop. Это может быть сложнее, чем вы думаете, потому что если в setUp возникает исключение, то tearDown не вызывается. unittest.TestCase.addCleanup() упрощает задачу:

>>> class MyTest(unittest.TestCase):
...     def setUp(self):
...         patcher = patch('package.module.Class')
...         self.MockClass = patcher.start()
...         self.addCleanup(patcher.stop)
...
...     def test_something(self):
...         assert package.module.Class is self.MockClass
...

В качестве дополнительного бонуса вам больше не нужно сохранять ссылку на объект patcher.

Также можно остановить все запущенные исправления, используя patch.stopall().

patch.stopall()

Останавливает все активные исправления. Останавливает только исправления, начинающиеся с start.

встроенные патчи

Вы можете исправлять любые здания внутри модуля. В следующем примере исправления встроены в ord():

>>> @patch('__main__.ord')
... def test(mock_ord):
...     mock_ord.return_value = 101
...     print(ord('c'))
...
>>> test()
101

ТЕСТ_ПРЕФИКС

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

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

Представьте, что у нас есть проект, который мы хотим протестировать со следующей структурой:

a.py
    -> Defines SomeClass

b.py
    -> from a import SomeClass
    -> some_function instantiates SomeClass

Теперь мы хотим протестировать some_function, но мы хотим имитировать SomeClass, используя patch(). Проблема в том, что когда мы импортируем модуль b, что нам нужно будет сделать, он импортирует SomeClass из модуля a. Если мы используем patch() для макетирования a.SomeClass, то это никак не повлияет на наш тест; в модуле b уже есть ссылка на реальный SomeClass, и, похоже, наше исправление не дало эффекта.

Главное - исправить SomeClass там, где он используется (или где его ищут). В этом случае some_function на самом деле будет искать SomeClass в модуле b, куда мы его импортировали. Исправление должно выглядеть следующим образом:

@patch('b.SomeClass')

Однако рассмотрим альтернативный сценарий, в котором вместо from a import SomeClass модуль b выполняет import a, а some_function использует a.SomeClass. Обе эти формы импорта являются общими. В этом случае класс, который мы хотим исправить, ищется в модуле, и поэтому вместо этого мы должны исправить a.SomeClass:

@patch('a.SomeClass')

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

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

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

Издевательские магические методы

Mock поддерживает имитацию методов протокола Python, также известную как «magic methods». Это позволяет имитировать объекты для замены контейнеров или других объектов, реализующих протоколы 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__, __truediv__, __floordiv__, __mod__, __divmod__, __lshift__, __rshift__, __and__, __xor__, __or__, и __pow__

  • Методы числового преобразования: __complex__, __int__, __float__ и __index__

  • Методы-дескрипторы: __get__, __set__ и __delete__

  • Маринование: __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate__ и __setstate__

  • Представление пути к файловой системе: __fspath__

  • Методы асинхронной итерации: __aiter__ и __anext__

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

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

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

  • __getattr__, __setattr__, __init__ и __new__

  • __prepare__, __instancecheck__, __subclasscheck__, __del__

Волшебная пародия

Существует два варианта MagicMock: MagicMock и NonCallableMagicMock.

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

MagicMock является подклассом Mock с реализациями по умолчанию большинства magic methods. Вы можете использовать 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__: значение str по умолчанию для макета

  • __sizeof__: размер макета по умолчанию

Например:

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

Два метода определения равенства, __eq__() и __ne__(), являются особыми. Они выполняют сравнение равенства по умолчанию для identity, используя атрибут 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__

Помощники

дозорный

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
>>> result
sentinel.some_object

по умолчанию

unittest.mock.DEFAULT

Объект DEFAULT является предварительно созданным сигналом (на самом деле sentinel.DEFAULT). Он может использоваться функциями side_effect для указания того, что следует использовать обычное возвращаемое значение.

вызов

unittest.mock.call(*args, **kwargs)

call() является вспомогательным объектом для создания более простых утверждений, для сравнения с call_args, call_args_list, mock_calls и method_calls. call() также может использоваться с assert_has_calls().

>>> m = MagicMock(return_value=None)
>>> m(1, 2, a='foo', b='bar')
>>> m()
>>> m.call_args_list == [call(1, 2, a='foo', b='bar'), call()]
True
call.call_list()

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

call_list особенно полезен для создания утверждений о «цепных вызовах». Цепной вызов - это несколько вызовов в одной строке кода. Это приводит к появлению нескольких записей в mock_calls в макете. Ручное построение последовательности вызовов может быть утомительным занятием.

call_list() может создавать последовательность вызовов из одного и того же связанного вызова:

>>> m = MagicMock()
>>> m(1).method(arg='foo').other('bar')(2.0)
<MagicMock name='mock().method().other()()' id='...'>
>>> kall = call(1).method(arg='foo').other('bar')(2.0)
>>> kall.call_list()
[call(1),
 call().method(arg='foo'),
 call().method().other('bar'),
 call().method().other()(2.0)]
>>> m.mock_calls == kall.call_list()
True

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

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

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

>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> 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

создать_автоспец

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

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

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

Если значение spec_set равно True, то при попытке установить атрибуты, которые не существуют в объекте spec, будет получено значение AttributeError.

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

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

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

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

какой-нибудь

unittest.mock.ANY

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

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

ANY не ограничивается сравнениями с объектами call и поэтому также может использоваться в тестовых утверждениях:

class TestStringMethods(unittest.TestCase):

    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', ANY])

ФИЛЬТР_DIR

unittest.mock.FILTER_DIR

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

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

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

макет_открыть

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 в mock. Это ограничивает api mocks api исходного объекта (спецификации), но оно рекурсивно (реализуется лениво), так что атрибуты mocks имеют только тот же api, что и атрибуты спецификации. Кроме того, методы имитируемых функций имеют ту же сигнатуру вызова, что и исходные, поэтому при неправильном вызове они выдают TypeError.

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

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

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

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

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

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

Ваши тесты могут пройти незаметно и неправильно из-за опечатки.

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

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

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

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

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

>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with()  # Intentional typo!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

class Something:
    a = 33

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

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

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

>>> class Something:
...   def __init__(self):
...     self.a = 33
...
>>> class SomethingForTest(Something):
...   a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>

Запечатывающие насмешки

unittest.mock.seal(mock)

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

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

>>> mock = Mock()
>>> mock.submock.attribute1 = 2
>>> mock.not_submock = mock.Mock(name="sample_name")
>>> seal(mock)
>>> mock.new_attribute  # This will raise AttributeError.
>>> mock.submock.attribute2  # This will raise AttributeError.
>>> mock.not_submock.attribute2  # This won't raise.

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

Порядок очередности side_effect, return_value и обертывания

Порядок их следования таков:

  1. side_effect

  2. return_value

  3. обертывания

Если заданы все три параметра, mock вернет значение из side_effect, полностью игнорируя return_value и обернутый объект. Если заданы какие-либо два параметра, значение вернет тот, который имеет более высокий приоритет. Независимо от того, какой порядок был установлен первым, порядок приоритета остается неизменным.

>>> from unittest.mock import Mock
>>> class Order:
...     @staticmethod
...     def get_value():
...         return "third"
...
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first'

Поскольку None является значением по умолчанию side_effect, если вы переназначите его значение обратно на None, порядок приоритета будет проверяться между return_value и обернутым объектом, игнорируя side_effect.

>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'

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

>>> from unittest.mock import DEFAULT
>>> order_mock.get_value.side_effect = [DEFAULT]
>>> order_mock.get_value()
'second'

Когда Mock обертывает объект, значением по умолчанию return_value будет DEFAULT.

>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.return_value
sentinel.DEFAULT
>>> order_mock.get_value.return_value
sentinel.DEFAULT

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

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

>>> order_mock_instance = order_mock()
>>> isinstance(order_mock_instance, Order)
True
>>> order_mock_instance.get_value()
'third'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'second'

Но если вы присвоите ему None, это не будет проигнорировано, поскольку это явное присвоение. Таким образом, порядок приоритета не будет изменен на обернутый объект.

>>> order_mock.get_value.return_value = None
>>> order_mock.get_value() is None
True

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

>>> order_mock = Mock(spec=Order, wraps=Order,
...                   **{"get_value.side_effect": ["first"],
...                      "get_value.return_value": "second"}
...                   )
...
>>> order_mock.get_value()
'first'
>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'

Если значение side_effect исчерпано, то порядок приоритета не приведет к получению значения от последователей. Вместо этого возникает исключение StopIteration.

>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first side effect value",
...                                     "another side effect value"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first side effect value'
>>> order_mock.get_value()
'another side effect value'
>>> order_mock.get_value()
Traceback (most recent call last):
 ...
StopIteration
Вернуться на верх