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 passisinstance()
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
. Passingunsafe=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
resetsreturn_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
resetsside_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 thereturn_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 aspec
,__class__
returns the spec class instead. This allows mock objects to passisinstance()
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 anisinstance()
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. TheThreadingMock
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 theThreadingMock.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 withpatch.TEST_PREFIX
. By default this is'test'
, which matches the wayunittest
finds tests. You can specify an alternative prefix by settingpatch.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()
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 ofMock
with default implementations of most of the magic methods. You can useMagicMock
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__
Чарівні методи слід шукати в класі, а не в екземплярі. Різні версії Python несумісні щодо застосування цього правила. Підтримувані методи протоколу мають працювати з усіма підтримуваними версіями Python.
Функція в основному підключена до класу, але кожен екземпляр Mock
зберігається ізольованим від інших.
Помічники¶
дозорний¶
- unittest.mock.sentinel¶
Об’єкт
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()
, andreadlines()
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()
andreadlines()
support. The mock ofread()
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='...'>
Це стосується лише класів або вже створених об’єктів. Виклик фіктивного класу для створення фальшивого екземпляра не створює справжній екземпляр. Виконуються лише пошуки атрибутів разом із викликами dir()
.
Герметизація макетів¶
- 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:
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