assert_has_calls
это другой подход к этой проблеме.
Из документов:
assert_has_calls (звонки, any_order = False)
Утвердите, что макет был вызван с указанными вызовами. Список mock_calls проверяется на наличие вызовов.
Если any_order имеет значение False (по умолчанию), то вызовы должны быть последовательными. Могут быть дополнительные звонки до или после указанных звонков.
Если any_order имеет значение True, то вызовы могут быть в любом порядке, но все они должны появляться в mock_calls.
Пример:
>>> from unittest.mock import call, Mock
>>> 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)
Источник: https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_has_calls
tuple
:isinstance(mock.call(1), tuple)
даетTrue
. Они также добавили некоторые методы и атрибуты.side_effect
Обычно меня не волнует порядок звонков, только то, что они произошли. В этом случае я объединяю
assert_any_call
с утверждением оcall_count
.Я считаю, что делать это таким образом легче для чтения и понимания, чем большой список вызовов, передаваемых в один метод.
Если вы заботитесь о заказе или ожидаете несколько идентичных звонков,
assert_has_calls
может быть более подходящим.редактировать
С тех пор как я опубликовал этот ответ, я переосмыслил свой подход к тестированию в целом. Я думаю, что стоит упомянуть, что если ваш тест усложняется, возможно, вы тестируете не по назначению или у вас проблемы с дизайном. Макеты предназначены для тестирования межобъектной коммуникации в объектно-ориентированном дизайне. Если ваш дизайн не ориентирован на возражения (как в более процедурном или функциональном), макет может быть совершенно неуместным. Возможно, внутри метода слишком много всего происходит, или вы можете тестировать внутренние детали, которые лучше не менять. Я разработал стратегию, упомянутую в этом методе, когда мой код был не очень объектно-ориентированным, и я считаю, что я также тестировал внутренние детали, которые лучше было бы оставить без проверки.
источник
do() if TEST_ENV=='prod' else dont()
), легко достигается путем насмешки, как вы предлагали. побочным эффектом этого является поддержание тестов по версиям (скажем, изменения кода между поиском Google api v1 и v2, ваш код будет тестировать версию 1, несмотря ни на что)Вы можете использовать
Mock.call_args_list
атрибут для сравнения параметров с предыдущими вызовами методов. Это в сочетании сMock.call_count
атрибутом должно дать вам полный контроль.источник
assert_has_calls
проверяет только, были ли выполнены ожидаемые вызовы, но не проверяет, являются ли они единственными.Я всегда должен искать это снова и снова, так что вот мой ответ.
Утверждение нескольких вызовов методов для разных объектов одного и того же класса
Предположим, у нас есть класс для работы в тяжелых условиях (который мы хотим издеваться):
Вот некоторый код, который использует два экземпляра
HeavyDuty
класса:Теперь вот контрольный пример для
heavy_work
функции:Мы издеваемся над
HeavyDuty
классомMockHeavyDuty
. Чтобы утверждать вызовы методов, поступающие из каждогоHeavyDuty
экземпляра, на который мы должны ссылатьсяMockHeavyDuty.return_value.assert_has_calls
, а неMockHeavyDuty.assert_has_calls
. Кроме того, в спискеexpected_calls
мы должны указать, какое имя метода нам нужно для вызова вызовов. Таким образом, наш список состоит из звонковcall.do_work
, а не простоcall
.Выполнение тестового примера показывает нам, что это успешно:
Если мы изменим
heavy_work
функцию, тест не пройдёт и выдаст полезное сообщение об ошибке:Утверждение нескольких вызовов функции
В отличие от вышеизложенного, вот пример, который показывает, как смоделировать несколько вызовов функции:
Есть два основных различия. Первый заключается в том, что при насмешке над функцией мы устанавливаем ожидаемые вызовы с помощью
call
, а не с помощьюcall.some_method
. Второй является то , что мы называемassert_has_calls
наmock_work_function
, а не наmock_work_function.return_value
.источник