unittest.mock — mock object library

Added in version 3.3.

Вихідний код: Lib/unittest/mock.py


unittest.mock — це бібліотека для тестування на Python. Це дозволяє вам замінити частини вашої тестованої системи на фіктивні об’єкти та зробити твердження про те, як вони використовувалися.

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

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

Mock розроблений для використання з unittest і базується на шаблоні «action -> assertion» замість «record -> replay», який використовується багатьма фреймворками мокінгу.

There is a backport of unittest.mock for earlier versions of Python, available as 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 allows you to perform side effects, including raising an exception when a mock is called:

>>> from unittest.mock import 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 налаштовує макет на отримання специфікації з іншого об’єкта. Спроба отримати доступ до атрибутів або методів у макеті, які не існують у специфікації, закінчиться помилкою з AttributeError.

Декоратор/менеджер контексту patch() дозволяє легко імітувати класи чи об’єкти в тестованому модулі. Вказаний вами об’єкт буде замінено макетом (або іншим об’єктом) під час тесту та відновлено після завершення тесту:

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

Примітка

Коли ви вкладаєте декоратори латок, макети передаються до декорованої функції в тому ж порядку, в якому вони застосовані (звичайний Python порядок застосування декораторів). Це означає знизу вгору, тому у наведеному вище прикладі спочатку передається макет для module.ClassName1.

З patch() важливо, щоб ви латали об’єкти в просторі імен, де вони шукаються. Зазвичай це просто, але для короткого посібника прочитайте де патч.

Як і декоратор patch() можна використовувати як менеджер контексту в операторі with:

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

Існує також patch.dict() для встановлення значень у словнику лише під час області видимості та відновлення словника до початкового стану після завершення тесту:

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

Mock підтримує знущання над Python магічні методи. Найпростіший спосіб використання магічних методів — це клас MagicMock. Це дозволяє вам робити такі речі, як:

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

Mock дозволяє вам призначати функції (або інші екземпляри Mock) магічним методам, і вони будуть викликані належним чином. Клас MagicMock — це просто варіант Mock, який містить усі магічні методи, попередньо створені для вас (ну, у всякому разі, всі корисні).

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

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

Щоб переконатися, що макетні об’єкти у ваших тестах мають той самий API, що й об’єкти, які вони замінюють, ви можете використати auto-speccing. Автоматичну специфікацію можна виконати за допомогою аргументу autospec для виправлення або функції create_autospec(). Автоматична специфікація створює макетні об’єкти, які мають ті самі атрибути та методи, що й об’єкти, які вони замінюють, а будь-які функції та методи (включаючи конструктори) мають ту саму сигнатуру виклику, що й реальний об’єкт.

Це гарантує, що ваші макети не працюватимуть так само, як і ваш робочий код, якщо вони використовуються неправильно:

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

create_autospec() також можна використовувати в класах, де він копіює сигнатуру методу __init__, і на викликаних об’єктах, де він копіює сигнатуру __call__ методу.

Імітаційний клас

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

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

Декоратори patch() дозволяють легко тимчасово замінити класи в певному модулі на об’єкт Mock. За умовчанням patch() створить для вас MagicMock. Ви можете вказати альтернативний клас Mock за допомогою аргументу new_callable для patch().

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

Створіть новий об’єкт Mock. Mock приймає кілька додаткових аргументів, які визначають поведінку об’єкта Mock:

  • spec: це може бути або список рядків, або існуючий об’єкт (клас або екземпляр), який діє як специфікація для макетного об’єкта. Якщо ви передаєте об’єкт, тоді список рядків формується шляхом виклику dir для об’єкта (за винятком непідтримуваних магічних атрибутів і методів). Доступ до будь-якого атрибута, якого немає в цьому списку, викличе AttributeError.

    If spec is an object (rather than a list of strings) then __class__ returns the class of the spec object. This allows mocks to pass isinstance() tests.

  • spec_set: суворіший варіант spec. У разі використання спроба встановити або отримати атрибут у макеті, якого немає в об’єкті, переданому як spec_set, викличе AttributeError.

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

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

    Якщо side_effect є iterable, тоді кожен виклик mock повертатиме наступне значення з iterable.

    Побічний_ефект можна очистити, встановивши для нього значення None.

  • return_value: значення, що повертається під час виклику макета. За замовчуванням це новий макет (створений під час першого доступу). Перегляньте атрибут return_value.

  • unsafe: By default, accessing any attribute whose name starts with assert, assret, asert, aseert or assrt will raise an AttributeError. Passing unsafe=True will allow access to these attributes.

    Added in version 3.5.

  • wraps: Елемент для обгортання фіктивного об’єкта. Якщо wraps не None, тоді виклик Mock передасть виклик оберненому об’єкту (повертаючи реальний результат). Доступ до атрибутів у макеті поверне об’єкт Mock, який обгортає відповідний атрибут упакованого об’єкта (тому спроба отримати доступ до атрибута, якого не існує, викличе AttributeError).

    Якщо макет має явний набір return_value, тоді виклики не передаються до упакованого об’єкта, а замість нього повертається return_value.

  • ім’я: якщо макет має ім’я, воно використовуватиметься у відтворенні макета. Це може бути корисним для налагодження. Ім’я поширюється на дитячі глузування.

Mocks також можна викликати з довільними ключовими аргументами. Вони використовуватимуться для встановлення атрибутів макета після його створення. Дивіться метод configure_mock() для отримання детальної інформації.

assert_called()

Стверджуйте, що макет був викликаний принаймні один раз.

>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called()

Added in version 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.
Calls: [call(), call()].

Added in version 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.
Calls: [call('foo', bar='baz'), call('other', bar='values')].
assert_any_call(*args, **kwargs)

стверджувати, що макет був викликаний із зазначеними аргументами.

Твердження проходить, якщо макет колись викликався, на відміну від assert_ called_with() і assert_ called_once_with(), які проходять, лише якщо виклик є останнім, і у випадку assert_ called_once_with() це також має бути єдиний виклик.

>>> mock = Mock(return_value=None)
>>> mock(1, 2, arg='thing')
>>> mock('some', 'thing', 'else')
>>> mock.assert_any_call(1, 2, arg='thing')
assert_has_calls(calls, any_order=False)

стверджувати, що макет було викликано за допомогою вказаних викликів. Список mock_calls перевіряється на наявність викликів.

Якщо any_order має значення false, виклики мають бути послідовними. До або після вказаних викликів можуть бути додаткові дзвінки.

Якщо any_order має значення true, виклики можуть бути в будь-якому порядку, але всі вони мають відображатися в mock_calls.

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

Стверджуйте, що макет ніколи не викликався.

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

Added in version 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

This can be useful where you want to make a series of assertions that reuse the same object.

return_value parameter when set to True resets return_value:

>>> mock = Mock(return_value=5)
>>> mock('hello')
5
>>> mock.reset_mock(return_value=True)
>>> mock('hello')
<Mock name='mock()' id='...'>

side_effect parameter when set to True resets side_effect:

>>> mock = Mock(side_effect=ValueError)
>>> mock('hello')
Traceback (most recent call last):
  ...
ValueError
>>> mock.reset_mock(side_effect=True)
>>> mock('hello')
<Mock name='mock()' id='...'>

Note that reset_mock() doesn’t clear the return_value, side_effect or any child attributes you have set using normal assignment by default.

Child mocks are reset as well.

Змінено в версії 3.6: Added two keyword-only arguments to the reset_mock function.

mock_add_spec(spec, spec_set=False)

Додайте специфікацію до макета. 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()
>>> mock.return_value = 'fish'
>>> mock()
'fish'

Типовим значенням, що повертається, є макет об’єкта, і ви можете налаштувати його звичайним способом:

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

return_value також можна встановити в конструкторі:

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

Це може бути або функція, яку потрібно викликати під час виклику макета, ітерація або виняток (клас або екземпляр), який потрібно викликати.

Якщо ви передаєте функцію, вона буде викликана з тими самими аргументами, що й макет, і якщо функція не повертає синглтон DEFAULT, виклик макету поверне те, що повертає функція. Якщо функція повертає DEFAULT, макет поверне своє нормальне значення (з return_value).

Якщо ви передаєте ітератор, він використовується для отримання ітератора, який повинен видавати значення під час кожного виклику. Це значення може бути або екземпляром винятку, який буде створено, або значенням, яке буде повернуто викликом 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 можна встановити в конструкторі. Ось приклад, який додає одиницю до значення, з яким викликається макет, і повертає його:

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

Встановлення side_effect на None очищає його:

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

Це або None (якщо макет не було викликано), або аргументи, з якими макет було викликано востаннє. Це буде у формі кортежу: перший член, до якого також можна отримати доступ через властивість args, — це будь-які впорядковані аргументи, з якими було викликано макет (або порожній кортеж), а другий член, який може доступ також через властивість kwargs, це будь-які ключові аргументи (або порожній словник).

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

call_args разом із членами списків call_args_list, method_calls і mock_calls є об’єктами call. Це кортежі, тому їх можна розпакувати, щоб отримати окремі аргументи та зробити складніші твердження. Перегляньте виклики як кортежі.

Змінено в версії 3.8: Додано властивості args і kwargs.

call_args_list

Це список усіх послідовних викликів макетного об’єкта (тому довжина списку — це кількість викликів до нього). До здійснення будь-яких викликів це порожній список. Об’єкт call можна використовувати для зручного створення списків викликів для порівняння з call_args_list.

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

Члени call_args_list є об’єктами call. Їх можна розпакувати як кортежі, щоб отримати окремі аргументи. Перегляньте виклики як кортежі.

method_calls

Крім відстеження викликів самих себе, mocks також відстежують виклики методів і атрибутів, а також їх методів і атрибутів:

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

Члени method_calls є об’єктами call. Їх можна розпакувати як кортежі, щоб отримати окремі аргументи. Перегляньте виклики як кортежі.

mock_calls

mock_calls записує всі виклики макетного об’єкта, його методи, чарівні методи та імітації значення, що повертається.

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

Члени mock_calls є об’єктами call. Їх можна розпакувати як кортежі, щоб отримати окремі аргументи. Перегляньте виклики як кортежі.

Примітка

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

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

Normally the __class__ attribute of an object will return its type. For a mock object with a spec, __class__ returns the spec class instead. This allows mock objects to pass isinstance() tests for the object they are replacing / masquerading as:

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

__class__ is assignable to, this allows a mock to pass an isinstance() check without forcing you to use a spec:

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

Версія Mock, яка не викликається. Параметри конструктора мають те саме значення, що й Mock, за винятком return_value і side_effect, які не мають значення для макету, який не викликається.

Mock objects that use a class or an instance as a spec or spec_set are able to pass isinstance() tests:

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

Класи Mock підтримують методи насміхуватої магії. Дивіться магічні методи для повної інформації.

Імітаційні класи та декоратори patch() приймають довільні ключові аргументи для налаштування. Для декораторів patch() ключові слова передаються до конструктора макета, який створюється. Ключові аргументи призначені для налаштування атрибутів макета:

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

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

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

Викликаний макет, створений за допомогою spec (або spec_set), перевіряє підпис об’єкта специфікації під час зіставлення викликів з макетом. Таким чином, він може відповідати фактичним аргументам виклику незалежно від того, чи були вони передані позиційно чи за назвою:

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

Це стосується assert_ called_with(), assert_ called_once_with(), assert_has_calls() і assert_any_call(). Коли Автоматична специфікація, це також застосовуватиметься до викликів методів фіктивного об’єкта.

Змінено в версії 3.4: Додано авторську перевірку підпису на специфіковані та автоматично специфіковані макетні об’єкти.

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

A mock intended to be used as a property, or other descriptor, on a class. PropertyMock provides __get__() and __set__() methods so you can specify a return value when it is fetched.

Отримання екземпляра PropertyMock з об’єкта викликає макет без аргументів. Його встановлення викликає макет із встановленим значенням.

>>> class Foo:
...     @property
...     def foo(self):
...         return 'something'
...     @foo.setter
...     def foo(self, value):
...         pass
...
>>> with patch('__main__.Foo.foo', new_callable=PropertyMock) as mock_foo:
...     mock_foo.return_value = 'mockity-mock'
...     this_foo = Foo()
...     print(this_foo.foo)
...     this_foo.foo = 6
...
mockity-mock
>>> mock_foo.mock_calls
[call(), call(6)]

Через те, як зберігаються макетні атрибути, ви не можете безпосередньо прикріпити PropertyMock до макетного об’єкта. Замість цього ви можете прикріпити його до об’єкта макетного типу:

>>> m = MagicMock()
>>> p = PropertyMock(return_value=3)
>>> type(m).foo = p
>>> m.foo
3
>>> p.assert_called_once_with()

Застереження

If an AttributeError is raised by PropertyMock, it will be interpreted as a missing descriptor and __getattr__() will be called on the parent mock:

>>> m = MagicMock()
>>> no_attribute = PropertyMock(side_effect=AttributeError)
>>> type(m).my_property = no_attribute
>>> m.my_property
<MagicMock name='mock.my_property' id='140165240345424'>

See __getattr__() for details.

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 є винятком, функція async викличе виняток,

  • якщо side_effect є ітерованим, функція async поверне наступне значення ітерованого, однак, якщо послідовність результатів вичерпана, StopAsyncIteration викликається негайно,

  • якщо side_effect не визначено, функція async поверне значення, визначене return_value, отже, за замовчуванням функція async повертає новий об’єкт AsyncMock.

Встановлення специфікації Mock або MagicMock на асинхронну функцію призведе до повернення об’єкта співпрограми після виклику.

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

Встановлення специфікації 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='...'>

Added in version 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.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 await not found.
Expected: mock('other')
Actual: mock('foo', bar='bar')
assert_awaited_once_with(*args, **kwargs)

Стверджуйте, що макет очікувався рівно один раз і з вказаними аргументами.

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

Стверджуйте, що макет коли-небудь був очікуваний із зазначеними аргументами.

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

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

Якщо any_order має значення false, очікування мають бути послідовними. Можуть бути додаткові виклики до або після вказаних очікувань.

Якщо any_order має значення true, очікування можуть бути в будь-якому порядку, але всі вони мають відображатися в await_args_list.

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

Стверджують, що макет так і не дочекався.

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

Перегляньте Mock.reset_mock(). Також встановлює await_count на 0, await_args на None і очищає await_args_list.

await_count

Ціле число, яке відстежує, скільки разів очікувався макетний об’єкт.

>>> mock = 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')]
class unittest.mock.ThreadingMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, *, timeout=UNSET, **kwargs)

A version of MagicMock for multithreading tests. The ThreadingMock object provides extra methods to wait for a call to be invoked, rather than assert on it immediately.

The default timeout is specified by the timeout argument, or if unset by the ThreadingMock.DEFAULT_TIMEOUT attribute, which defaults to blocking (None).

You can configure the global default timeout by setting ThreadingMock.DEFAULT_TIMEOUT.

wait_until_called(*, timeout=UNSET)

Waits until the mock is called.

If a timeout was passed at the creation of the mock or if a timeout argument is passed to this function, the function raises an AssertionError if the call is not performed in time.

>>> mock = ThreadingMock()
>>> thread = threading.Thread(target=mock)
>>> thread.start()
>>> mock.wait_until_called(timeout=1)
>>> thread.join()
wait_until_any_call_with(*args, **kwargs)

Waits until the mock is called with the specified arguments.

If a timeout was passed at the creation of the mock the function raises an AssertionError if the call is not performed in time.

>>> mock = ThreadingMock()
>>> thread = threading.Thread(target=mock, args=("arg1", "arg2",), kwargs={"arg": "thing"})
>>> thread.start()
>>> mock.wait_until_any_call_with("arg1", "arg2", arg="thing")
>>> thread.join()
DEFAULT_TIMEOUT

Global default timeout in seconds to create instances of ThreadingMock.

Added in version 3.13.

Дзвінок

Фіктивні об’єкти можна викликати. Виклик поверне значення, встановлене як атрибут return_value. Типовим значенням, що повертається, є новий об’єкт Mock; воно створюється під час першого доступу до значення, що повертається (явним чином або шляхом виклику Mock), але воно зберігається й кожного разу повертається те саме.

Виклики, здійснені до об’єкта, будуть записані в такі атрибути, як call_args і call_args_list.

If side_effect is set then it will be called after the call has been recorded, so if side_effect raises an exception the call is still recorded.

Найпростіший спосіб змусити макет викликати виняток під час виклику — це зробити 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')]

If side_effect is a function then whatever that function returns is what calls to the mock return. The side_effect function is called with the same arguments as the mock. This allows you to vary the return value of the call dynamically, based on the input:

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

If you want the mock to still return the default return value (a new mock), or any set return value, then there are two ways of doing this. Either return return_value from inside side_effect, or return 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

To remove a side_effect, and return to the default behaviour, set the side_effect to 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

The side_effect can also be any iterable object. Repeated calls to the mock will return values from the iterable (until the iterable is exhausted and a StopIteration is raised):

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

Видалення атрибутів

Макетні об’єкти створюють атрибути на вимогу. Це дозволяє їм видавати себе за об’єкти будь-якого типу.

You may want a mock object to return False to a hasattr() call, or raise an AttributeError when an attribute is fetched. You can do this by providing an object as a spec for a mock, but that isn’t always convenient.

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

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

Макетні назви та атрибут name

Оскільки «name» є аргументом конструктора Mock, якщо ви хочете, щоб ваш макетний об’єкт мав атрибут «name», ви не можете просто передати його під час створення. Є дві альтернативи. Одним із варіантів є використання configure_mock():

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

Простішим варіантом є просто встановити атрибут «name» після створення макета:

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

Додавання макетів як атрибутів

Коли ви додаєте макет як атрибут іншого макету (або як значення, що повертається), він стає «дочірнім» для цього макету. Виклики дочірнього елемента записуються в атрибутах method_calls і mock_calls батьківського. Це корисно для конфігурації дочірніх імітацій, а потім прикріплення їх до батьківського, або для прикріплення імітацій до батьківського, який записує всі виклики до нащадків і дозволяє вам робити твердження щодо порядку викликів між імітаціями:

>>> parent = MagicMock()
>>> child1 = MagicMock(return_value=None)
>>> child2 = MagicMock(return_value=None)
>>> parent.child1 = child1
>>> parent.child2 = child2
>>> child1(1)
>>> child2(2)
>>> parent.mock_calls
[call.child1(1), call.child2(2)]

Винятком є випадки, коли макет має назву. Це дозволяє запобігти «батьківству», якщо з якихось причин ви цього не хочете.

>>> mock = MagicMock()
>>> not_a_child = MagicMock(name='not-a-child')
>>> mock.attribute = not_a_child
>>> mock.attribute()
<MagicMock name='not-a-child()' id='...'>
>>> mock.mock_calls
[]

Макети, створені для вас patch(), автоматично отримують імена. Щоб приєднати імена до батьківського елемента, ви використовуєте метод attach_mock():

>>> thing1 = object()
>>> thing2 = object()
>>> parent = MagicMock()
>>> with patch('__main__.thing1', return_value=None) as child1:
...     with patch('__main__.thing2', return_value=None) as child2:
...         parent.attach_mock(child1, 'child1')
...         parent.attach_mock(child2, 'child2')
...         child1('one')
...         child2('two')
...
>>> parent.mock_calls
[call.child1('one'), call.child2('two')]

Патчери

Декоратори латок використовуються для латання об’єктів лише в межах функції, яку вони декорують. Вони автоматично виконують видалення виправлень за вас, навіть якщо виникають винятки. Усі ці функції також можна використовувати в операторах або як декоратори класів.

патч

Примітка

Головне — виконати виправлення у правильному просторі імен. Дивіться розділ where to patch.

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

patch() діє як декоратор функції, декоратор класу або менеджер контексту. Усередині тіла функції або оператора ціль додається до нового об’єкта. Коли оператор function/with завершує роботу, патч скасовується.

Якщо new пропущено, ціль замінюється на AsyncMock, якщо виправлений об’єкт є асинхронною функцією, або MagicMock інакше. Якщо patch() використовується як декоратор, а new опущено, створений макет передається як додатковий аргумент до декорованої функції. Якщо patch() використовується як менеджер контексту, створений макет повертається менеджером контексту.

ціль має бути рядком у формі 'package.module.ClassName. Ціль імпортується, а вказаний об’єкт замінюється на новий об’єкт, тому ціль має бути імпортованою із середовища, з якого ви викликаєте patch(). Ціль імпортується під час виконання декорованої функції, а не під час декорування.

Ключові аргументи spec і spec_set передаються в MagicMock, якщо patch створює його для вас.

Крім того, ви можете передати spec=True або spec_set=True, що спричиняє передачу латки в об’єкті, який імітується, як об’єкт spec/spec_set.

new_callable дозволяє вказати інший клас або викликаний об’єкт, який буде викликано для створення нового об’єкта. За замовчуванням AsyncMock використовується для асинхронних функцій, а MagicMock для решти.

Більш потужною формою spec є autospec. Якщо ви встановите autospec=True, макет буде створено зі специфікацією об’єкта, який замінюється. Усі атрибути макета також матимуть специфікацію відповідного атрибута об’єкта, який замінюється. Методи та функції, які знущаються, перевірятимуть аргументи та викличуть TypeError, якщо вони викликані з неправильним підписом. Для моків, які замінюють клас, їх значення, що повертається («екземпляр») матиме ту саму специфікацію, що й клас. Перегляньте функцію create_autospec() і Автоматична специфікація.

Замість autospec=True ви можете передати autospec=some_object, щоб використовувати довільний об’єкт як специфікацію замість того, що замінюється.

За замовчуванням patch() не зможе замінити атрибути, яких не існує. Якщо ви передаєте create=True, а атрибут не існує, patch створить атрибут для вас під час виклику виправленої функції та видалить його знову після завершення виправленої функції. Це корисно для написання тестів щодо атрибутів, які ваш робочий код створює під час виконання. За умовчанням його вимкнено, оскільки це може бути небезпечно. Якщо його ввімкнути, ви можете писати тести проходження проти API, яких насправді не існує!

Примітка

Змінено в версії 3.5: Якщо ви виправляєте вбудовані компоненти в модулі, вам не потрібно передавати create=True, він буде доданий за замовчуванням.

Patch can be used as a TestCase class decorator. It works by decorating each test method in the class. This reduces the boilerplate code when your test methods share a common patchings set. patch() finds tests by looking for method names that start with patch.TEST_PREFIX. By default this is 'test', which matches the way unittest finds tests. You can specify an alternative prefix by setting patch.TEST_PREFIX.

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

To configure return values on methods of instances on the patched class you must do this on the return_value. For example:

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

>>> 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. Будь-які довільні ключові слова, які ви передаєте у виклик, використовуватимуться для встановлення атрибутів створеного mock:

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

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

заправити названий член (атрибут) на об’єкті (ціль) за допомогою макетного об’єкта.

patch.object() можна використовувати як декоратор, декоратор класу або менеджер контексту. Аргументи new, spec, create, spec_set, autospec і new_callable мають те саме значення, що й для patch(). Подібно до patch(), patch.object() приймає довільні ключові аргументи для налаштування макетного об’єкта, який він створює.

При використанні як декоратора класу patch.object() враховує patch.TEST_PREFIX для вибору методів для обгортання.

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

Під час виклику з формою з двома аргументами ви пропускаєте об’єкт заміни, і для вас створюється макет, який передається як додатковий аргумент до декорованої функції:

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

spec, create та інші аргументи для patch.object() мають те саме значення, що й для patch().

patch.dict

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

Виправте словник або об’єкт, схожий на словник, і відновіть словник до початкового стану після тесту.

in_dict може бути словником або контейнером, схожим на відображення. Якщо це відображення, то воно повинно принаймні підтримувати отримання, встановлення та видалення елементів, а також ітерацію по ключах.

in_dict також може бути рядком, що визначає назву словника, який потім буде отримано шляхом його імпорту.

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

Якщо clear має значення true, словник буде очищено перед встановленням нових значень.

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

Змінено в версії 3.8: patch.dict() тепер повертає виправлений словник, коли використовується як менеджер контексту.

patch.dict() можна використовувати як контекстний менеджер, декоратор або декоратор класу:

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

Коли використовується як декоратор класу, patch.dict() враховує patch.TEST_PREFIX (за замовчуванням 'test') для вибору методів для обгортання:

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

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

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

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

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

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

patch.dict() can be used with dictionary like objects that aren’t actually dictionaries. At the very minimum they must support item getting, setting, deleting and either iteration or membership test. This corresponds to the magic methods __getitem__(), __setitem__(), __delitem__() and either __iter__() or __contains__().

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

patch.multiple

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

Виконайте кілька патчів за один виклик. Він приймає об’єкт, який потрібно виправити (або як об’єкт, або рядок для отримання об’єкта шляхом імпорту), а також ключові аргументи для патчів:

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

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

patch.multiple() можна використовувати як декоратор, декоратор класу або менеджер контексту. Аргументи spec, spec_set, create, autospec і new_callable мають те саме значення, що й для patch(). Ці аргументи будуть застосовані до всіх патчів, створених patch.multiple().

При використанні як декоратор класу patch.multiple() враховує patch.TEST_PREFIX для вибору методів для обгортання.

Якщо ви хочете, щоб patch.multiple() створював для вас макети, ви можете використовувати DEFAULT як значення. Якщо ви використовуєте patch.multiple() як декоратор, тоді створені макети передаються в декоровану функцію за ключовим словом.

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

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

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

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

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

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

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

All the patchers have start() and stop() methods. These make it simpler to do patching in setUp methods or where you want to do multiple patches without nesting decorators or with statements.

To use them call patch(), patch.object() or patch.dict() as normal and keep a reference to the returned patcher object. You can then call start() to put the patch in place and stop() to undo it.

Якщо ви використовуєте 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

A typical use case for this might be for doing multiple patches in the setUp method of a TestCase:

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

Застереження

Якщо ви використовуєте цю техніку, ви повинні переконатися, що виправлення «скасовано», викликавши stop. Це може бути складніше, ніж ви могли б подумати, оскільки якщо в setUp виникає виняток, tearDown не викликається. unittest.TestCase.addCleanup() полегшує це:

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

Як додатковий бонус, вам більше не потрібно зберігати посилання на об’єкт patcher.

Також можна зупинити всі запущені латки за допомогою patch.stopall().

patch.stopall()

Зупинити всі активні патчі. Зупиняє лише патчі, розпочаті з start.

вбудовані виправлення

Ви можете виправляти будь-які вбудовані компоненти в модулі. Наступні приклади патчів вбудовані в ord():

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

TEST_PREFIX

Усі патчери можна використовувати як декоратори класів. При такому використанні вони загортають кожен тестовий метод у клас. Патчери розпізнають методи, які починаються з 'test', як методи тестування. Таким же чином unittest.TestLoader знаходить тестові методи за замовчуванням.

Можливо, ви хочете використовувати інший префікс для своїх тестів. Ви можете повідомити патчерів про інший префікс, встановивши patch.TEST_PREFIX:

>>> patch.TEST_PREFIX = 'foo'
>>> value = 3
>>>
>>> @patch('__main__.value', 'not three')
... class Thing:
...     def foo_one(self):
...         print(value)
...     def foo_two(self):
...         print(value)
...
>>>
>>> Thing().foo_one()
not three
>>> Thing().foo_two()
not three
>>> value
3

Декоратори вкладених патчів

Якщо ви хочете виконати кілька патчів, ви можете просто скласти декоратори.

Ви можете скласти кілька декораторів патчів, використовуючи цей шаблон:

>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
...     assert SomeClass.static_method is mock1
...     assert SomeClass.class_method is mock2
...     SomeClass.static_method('foo')
...     SomeClass.class_method('bar')
...     return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')

Зверніть увагу, що декоратори наносяться знизу вгору. Це стандартний спосіб, у який Python застосовує декоратори. Порядок створених макетів, переданих у вашу тестову функцію, відповідає цьому порядку.

Де латати

patch() працює, (тимчасово) змінюючи об’єкт, на який вказує ім’я, на інший. Може бути багато імен, які вказують на будь-який окремий об’єкт, тому, щоб виправлення працювало, ви повинні переконатися, що ви виправляєте ім’я, яке використовує тестована система.

Основний принцип полягає в тому, що ви виправляєте там, де об’єкт переглядається, що не обов’язково є тим самим місцем, де він визначений. Кілька прикладів допоможуть прояснити це.

Уявіть, що у нас є проект, який ми хочемо перевірити з такою структурою:

a.py
    -> Defines SomeClass

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

Now we want to test some_function but we want to mock out SomeClass using patch(). The problem is that when we import module b, which we will have to do when it imports SomeClass from module a. If we use patch() to mock out a.SomeClass then it will have no effect on our test; module b already has a reference to the real SomeClass and it looks like our patching had no effect.

Головне – виправити SomeClass там, де він використовується (або де його шукають). У цьому випадку some_function фактично шукатиме SomeClass у модулі b, куди ми його імпортували. Виправлення має виглядати:

@patch('b.SomeClass')

Однак розглянемо альтернативний сценарій, де замість from a import SomeClass модуль b виконує імпорт a, а some_function використовує a.SomeClass. Обидві ці форми імпорту поширені. У цьому випадку клас, який ми хочемо виправити, шукається в модулі, тому ми повинні замість цього виправити a.SomeClass:

@patch('a.SomeClass')

Виправлення дескрипторів і проксі-об’єктів

Both patch and patch.object correctly patch and restore descriptors: class methods, static methods and properties. You should patch these on the class rather than an instance. They also work with some objects that proxy attribute access, like the django settings object.

Підтримка MagicMock і магічного методу

Знущальні магічні методи

Mock supports mocking the Python protocol methods, also known as «magic methods». This allows mock objects to replace containers or other objects that implement Python protocols.

Оскільки магічні методи шукаються інакше, ніж звичайні методи [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__

  • The numeric methods (including right hand and in-place variants): __add__, __sub__, __mul__, __matmul__, __truediv__, __floordiv__, __mod__, __divmod__, __lshift__, __rshift__, __and__, __xor__, __or__, and __pow__

  • Числові методи перетворення: __complex__, __int__, __float__ і __index__

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

  • Травлення: __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate__ і __setstate__

  • Представлення шляху файлової системи: __fspath__

  • Асинхронні методи ітерації: __aiter__ і __anext__

Змінено в версії 3.8: Додано підтримку для os.PathLike.__fspath__().

Змінено в версії 3.8: Додано підтримку __aenter__, __aexit__, __aiter__ і __anext__.

Наступні методи існують, але не підтримуються, оскільки вони або використовуються макетом, не можуть бути встановлені динамічно або можуть спричиняти проблеми:

  • __getattr__, __setattr__, __init__ і __new__

  • __prepare__, __instancecheck__, __subclasscheck__, __del__

Магічний макет

Є два варіанти MagicMock: MagicMock і NonCallableMagicMock.

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

MagicMock is a subclass of Mock with default implementations of most of the magic methods. You can use MagicMock without having to configure the magic methods yourself.

Параметри конструктора мають те саме значення, що й для 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__: Правда

  • __index__: 1

  • __hash__: типовий хеш для макета

  • __str__: рядок за умовчанням для макета

  • __sizeof__: розмір за замовчуванням для макету

Наприклад:

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

The two equality methods, __eq__() and __ne__(), are special. They do the default equality comparison on identity, using the side_effect attribute, unless you change their return value to return something else:

>>> 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 тепер зберігають свою ідентичність, коли їх скопіюють або маринують.

Іноді під час тестування вам потрібно перевірити, чи певний об’єкт передається як аргумент до іншого методу або повертається. Для перевірки цього може бути звичайним створення іменованих дозорних об’єктів. 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_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, які знаходяться в Mock.call_args, Mock.call_args_list і Mock.mock_calls атрибути можна перевірити, щоб отримати окремі аргументи, які вони містять.

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

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

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

create_autospec

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

Створіть макет об’єкта, використовуючи інший об’єкт як специфікацію. Атрибути на макеті використовуватимуть відповідний атрибут об’єкта spec як свою специфікацію.

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

Якщо spec_set має значення True, тоді спроба встановити атрибути, яких не існує в об’єкті специфікації, призведе до появи AttributeError.

Якщо клас використовується як специфікація, тоді значення, що повертається mock (екземпляр класу), матиме ту саму специфікацію. Ви можете використовувати клас як специфікацію для об’єкта екземпляра, передавши instance=True. Повернений макет можна буде викликати, лише якщо його екземпляри можна викликати.

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

Перегляньте Автоматична специфікація для прикладів використання автоматичної специфікації з create_autospec() і аргументом autospec для patch().

Змінено в версії 3.8: create_autospec() тепер повертає AsyncMock, якщо метою є асинхронна функція.

БУДЬ-ЯКИЙ

unittest.mock.ANY

Іноді вам може знадобитися зробити твердження щодо деяких аргументів у виклику mock, але ви або не дбаєте про деякі з аргументів, або хочете вилучити їх окремо з call_args і зробити складнішими твердження на них.

Щоб ігнорувати певні аргументи, ви можете передати об’єкти, порівняння яких дорівнює всему. Виклики assert_ called_with() і assert_ called_once_with() будуть успішними незалежно від того, що було передано.

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

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

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

ANY is not limited to comparisons with call objects and so can also be used in test assertions:

class TestStringMethods(unittest.TestCase):

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

FILTER_DIR

unittest.mock.FILTER_DIR

FILTER_DIR is a module level variable that controls the way mock objects respond to dir(). The default is True, which uses the filtering described below, to only show useful members. If you dislike this filtering, or need to switch it off for diagnostic purposes, then set mock.FILTER_DIR = False.

Якщо фільтрацію ввімкнено, dir(some_mock) показує лише корисні атрибути та включатиме будь-які динамічно створені атрибути, які зазвичай не відображаються. Якщо макет було створено за допомогою spec (або autospec, звичайно), тоді відображаються всі атрибути з оригіналу, навіть якщо до них ще не було доступу:

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

Багато з не дуже корисних (приватних для Mock, а не для того, що висміюється) атрибутів підкреслення та подвійного підкреслення з префіксом було відфільтровано з результату виклику dir() на Mock. Якщо вам не подобається така поведінка, ви можете вимкнути її, встановивши перемикач рівня модуля FILTER_DIR:

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

Alternatively you can just use vars(my_mock) (instance members) and dir(type(my_mock)) (type members) to bypass the filtering irrespective of FILTER_DIR.

mock_open

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

Допоміжна функція для створення макету замість використання open(). Це працює для open(), що викликається безпосередньо або використовується як контекстний менеджер.

Аргумент mock є макетним об’єктом для налаштування. Якщо None (за замовчуванням), тоді для вас буде створено MagicMock з API, обмеженим методами чи атрибутами, доступними для стандартних дескрипторів файлів.

read_data is a string for the read(), readline(), and readlines() methods of the file handle to return. Calls to those methods will take data from read_data until it is depleted. The mock of these methods is pretty simplistic: every time the mock is called, the read_data is rewound to the start. If you need more control over the data that you are feeding to the tested code you will need to customize this mock for yourself. When that is insufficient, one of the in-memory filesystem packages on PyPI can offer a realistic filesystem for testing.

Змінено в версії 3.4: Added readline() and readlines() support. The mock of read() changed to consume read_data rather than returning it on each call.

Змінено в версії 3.5: read_data тепер скидається під час кожного виклику mock.

Змінено в версії 3.8: Added __iter__() to implementation so that iteration (such as in for loops) correctly consumes read_data.

Використання open() як контекстного менеджера є чудовим способом переконатися, що ваші дескриптори файлів закриті належним чином, і стає поширеним:

with open('/some/path', 'w') as f:
    f.write('something')

The issue is that even if you mock out the call to open() it is the returned object that is used as a context manager (and has __enter__() and __exit__() called).

Знущання над контекстними менеджерами за допомогою 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'

Автоматична специфікація

Autospeccing is based on the existing spec feature of mock. It limits the api of mocks to the api of an original object (the spec), but it is recursive (implemented lazily) so that attributes of mocks only have the same api as the attributes of the spec. In addition mocked functions / methods have the same call signature as the original so they raise a TypeError if they are called incorrectly.

Перш ніж пояснювати, як працює автоматична специфікація, ось чому вона потрібна.

Mock is a very powerful and flexible object, but it suffers from a flaw which is general to mocking. If you refactor some of your code, rename members and so on, any tests for code that is still using the old api but uses mocks instead of the real objects will still pass. This means your tests can all pass even though your code is broken.

Змінено в версії 3.5: Before 3.5, tests with a typo in the word assert would silently pass when they should raise an error. You can still achieve this behavior by passing unsafe=True to Mock.

Зауважте, що це ще одна причина, чому вам потрібні інтеграційні тести, а також модульні тести. Тестувати все ізольовано — це чудово, але якщо ви не перевірятимете, як ваші пристрої «з’єднані між собою», все ще є багато місця для помилок, які могли виявити тести.

unittest.mock already provides a feature to help with this, called speccing. If you use a class or instance as the spec for a mock then you can only access attributes on the mock that exist on the real class:

>>> 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.header_items()
<mock.Mock object at 0x...>
>>> mock.header_items.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='...'>

You can see that request.Request has a spec. request.Request takes two arguments in the constructor (one of which is self). Here’s what happens if we try to call it incorrectly:

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

Специфікація також стосується екземплярів класів (тобто значення, що повертається specced mocks):

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

Request objects are not callable, so the return value of instantiating our mocked out request.Request is a non-callable mock. With the spec in place any typos in our asserts will raise the correct error:

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

A more serious problem is that it is common for instance attributes to be created in the __init__() method and not to exist on the class at all. autospec can’t know about any dynamically created attributes and restricts the api to visible attributes.

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

Probably the best way of solving the problem is to add class attributes as default values for instance members initialised in __init__(). Note that if you are only setting default attributes in __init__() then providing them via class attributes (shared between instances of course) is faster too. e.g.

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.

Added in version 3.7.

Order of precedence of side_effect, return_value and wraps

The order of their precedence is:

  1. side_effect

  2. return_value

  3. wraps

If all three are set, mock will return the value from side_effect, ignoring return_value and the wrapped object altogether. If any two are set, the one with the higher precedence will return the value. Regardless of the order of which was set first, the order of precedence remains unchanged.

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

As None is the default value of side_effect, if you reassign its value back to None, the order of precedence will be checked between return_value and the wrapped object, ignoring side_effect.

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

If the value being returned by side_effect is DEFAULT, it is ignored and the order of precedence moves to the successor to obtain the value to return.

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

When Mock wraps an object, the default value of return_value will be DEFAULT.

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

The order of precedence will ignore this value and it will move to the last successor which is the wrapped object.

As the real call is being made to the wrapped object, creating an instance of this mock will return the real instance of the class. The positional arguments, if any, required by the wrapped object must be passed.

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

But if you assign None to it, this will not be ignored as it is an explicit assignment. So, the order of precedence will not move to the wrapped object.

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

Even if you set all three at once when initializing the mock, the order of precedence remains the same:

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

If side_effect is exhausted, the order of precedence will not cause a value to be obtained from the successors. Instead, StopIteration exception is raised.

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