Из всего этого новое - самое запутанное. Благодаря экспериментированию новое ключевое слово похоже на предоставление разработчикам возможности переопределить реализацию наследующего класса с реализацией базового класса, явно определяя тип. Это похоже на то, как думать наоборот.
В приведенном ниже примере результат вернет «Производный результат» до тех пор, пока тип явно не будет определен как тест BaseClass, только тогда будет возвращен «Базовый результат».
class Program
{
static void Main(string[] args)
{
var test = new DerivedClass();
var result = test.DoSomething();
}
}
class BaseClass
{
public virtual string DoSomething()
{
return "Base result";
}
}
class DerivedClass : BaseClass
{
public new string DoSomething()
{
return "Derived result";
}
}
Вы можете использовать атрибут Mock.call_args_list
для сравнения параметров с предыдущими вызовами метода. Это в сочетании с атрибутом Mock.call_count
должно дать вам полный контроль.
Обычно меня не волнует порядок звонков, только то, что они произошли. В этом случае я комбинирую assert_any_call
с утверждением о call_count
.
>>> import mock
>>> m = mock.Mock()
>>> m(1)
<Mock name='mock()' id='37578160'>
>>> m(2)
<Mock name='mock()' id='37578160'>
>>> m(3)
<Mock name='mock()' id='37578160'>
>>> m.assert_any_call(1)
>>> m.assert_any_call(2)
>>> m.assert_any_call(3)
>>> assert 3 == m.call_count
>>> m.assert_any_call(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "[python path]\lib\site-packages\mock.py", line 891, in assert_any_call
'%s call not found' % expected_string
AssertionError: mock(4) call not found
Я считаю, что это делается проще читать и понимать, чем большой список вызовов, переданных в один метод.
Если вы заботитесь о порядке или ожидаете несколько идентичных вызовов, assert_has_calls
может быть более уместным .
Поскольку я опубликовал этот ответ, я переосмыслил свой подход к тестированию в целом. Я думаю, что стоит упомянуть, что если ваш тест усложняется, вы можете испытывать неуместно или иметь проблемы с дизайном. Mocks предназначены для тестирования межобъектной связи в объектно-ориентированном дизайне. Если ваш дизайн не ориентирован на возражения (как в более процедурном или функциональном), макет может быть совершенно неуместным. Возможно, вы слишком много работаете внутри метода, или вы можете тестировать внутренние детали, которые лучше всего оставить незафиксированными. Я разработал стратегию, упомянутую в этом методе, когда мой код был не очень объектно-ориентированным, и я считаю, что я также тестировал внутренние детали, которые лучше всего оставить незафиксированными.
do() if TEST_ENV=='prod' else dont()
), достигается легко, насмехаясь, как вы предлагали. побочным эффектом этого является поддержание тестов на версии (скажем, изменения кода между google-поиском api v1 и v2, ваш код проверяет версию 1 независимо от того, что)
– Daniel Dubovski
27 October 2016 в 12:43
Мне всегда нужно снова смотреть это снова и снова, так что вот мой ответ.
Предположим, что мы имеем класс большой нагрузки (который мы хотим высмеять):
In [1]: class HeavyDuty(object):
...: def __init__(self):
...: import time
...: time.sleep(2) # <- Spends a lot of time here
...:
...: def do_work(self, arg1, arg2):
...: print("Called with %r and %r" % (arg1, arg2))
...:
вот какой-то код, который использует два экземпляра класса HeavyDuty
:
In [2]: def heavy_work():
...: hd1 = HeavyDuty()
...: hd1.do_work(13, 17)
...: hd2 = HeavyDuty()
...: hd2.do_work(23, 29)
...:
Теперь, вот тестовый пример для heavy_work
:
In [3]: from unittest.mock import patch, call
...: def test_heavy_work():
...: expected_calls = [call.do_work(13, 17),call.do_work(23, 29)]
...:
...: with patch('__main__.HeavyDuty') as MockHeavyDuty:
...: heavy_work()
...: MockHeavyDuty.return_value.assert_has_calls(expected_calls)
...:
Мы издеваемся над классом HeavyDuty
с MockHeavyDuty
. Чтобы утверждать вызовы методов, исходящие из каждого экземпляра HeavyDuty
, мы должны ссылаться на MockHeavyDuty.return_value.assert_has_calls
вместо MockHeavyDuty.assert_has_calls
. Кроме того, в списке expected_calls
мы должны указать, какое имя метода мы заинтересованы в утверждении вызовов. Поэтому наш список состоит из вызовов call.do_work
, а не просто call
.
Выполнение тестового примера показывает, что он успешный:
In [4]: print(test_heavy_work())
None
Если мы изменим функцию heavy_work
, тест завершится неудачно и даст полезную информацию сообщение об ошибке:
In [5]: def heavy_work():
...: hd1 = HeavyDuty()
...: hd1.do_work(113, 117) # <- call args are different
...: hd2 = HeavyDuty()
...: hd2.do_work(123, 129) # <- call args are different
...:
In [6]: print(test_heavy_work())
---------------------------------------------------------------------------
(traceback omitted for clarity)
AssertionError: Calls not found.
Expected: [call.do_work(13, 17), call.do_work(23, 29)]
Actual: [call.do_work(113, 117), call.do_work(123, 129)]
Чтобы контрастировать с вышесказанным, приведен пример, который показывает, как издеваться над несколькими вызовами к функции:
In [7]: def work_function(arg1, arg2):
...: print("Called with args %r and %r" % (arg1, arg2))
In [8]: from unittest.mock import patch, call
...: def test_work_function():
...: expected_calls = [call(13, 17), call(23, 29)]
...: with patch('__main__.work_function') as mock_work_function:
...: work_function(13, 17)
...: work_function(23, 29)
...: mock_work_function.assert_has_calls(expected_calls)
...:
In [9]: print(test_work_function())
None
Существуют два основных отличия. Первый заключается в том, что при издевательстве функции мы устанавливаем наши ожидаемые вызовы с помощью call
вместо использования call.some_method
. Второй - это то, что мы назовем assert_has_calls
на mock_work_function
, а не на mock_work_function.return_value
.
assert_has_calls
- это еще один подход к этой проблеме.
Из документов:
assert_has_calls (вызовы, any_order = False)
утверждают, что mock был вызван с указанными вызовами. Список mock_calls проверяется для вызовов.
Если any_order имеет значение False (по умолчанию), вызовы должны быть последовательными. Дополнительные вызовы до или после указанных вызовов.
Если any_order имеет значение True, то вызовы могут быть в любом порядке, но все они должны отображаться в mock_calls.
blockquote>Пример:
>>> from 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)
Источник: http://www.voidspace.org.uk/python/mock/mock.html#mock.Mock.assert_has_calls
tuple
: isinstance(mock.call(1), tuple)
дает True
. Они также добавили некоторые методы и атрибуты.
– jpmc26
16 September 2015 в 04:50
assert_has_calls
проверяет только, были ли ожидаемые вызовы, но нет, если они являются единственными. – blueyed 1 June 2016 в 12:13