Как проверять количество вымышленных функций? [Дубликат]

Из всего этого новое - самое запутанное. Благодаря экспериментированию новое ключевое слово похоже на предоставление разработчикам возможности переопределить реализацию наследующего класса с реализацией базового класса, явно определяя тип. Это похоже на то, как думать наоборот.

В приведенном ниже примере результат вернет «Производный результат» до тех пор, пока тип явно не будет определен как тест 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";
    }
}
92
задан Jonathan 30 August 2011 в 13:21
поделиться

4 ответа

Вы можете использовать атрибут Mock.call_args_list для сравнения параметров с предыдущими вызовами метода. Это в сочетании с атрибутом Mock.call_count должно дать вам полный контроль.

31
ответ дан Jonathan 19 August 2018 в 00:21
поделиться
  • 1
    assert_has_calls ()? – bavaza 28 May 2012 в 14:52
  • 2
    assert_has_calls проверяет только, были ли ожидаемые вызовы, но нет, если они являются единственными. – blueyed 1 June 2016 в 12:13

Обычно меня не волнует порядок звонков, только то, что они произошли. В этом случае я комбинирую 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 предназначены для тестирования межобъектной связи в объектно-ориентированном дизайне. Если ваш дизайн не ориентирован на возражения (как в более процедурном или функциональном), макет может быть совершенно неуместным. Возможно, вы слишком много работаете внутри метода, или вы можете тестировать внутренние детали, которые лучше всего оставить незафиксированными. Я разработал стратегию, упомянутую в этом методе, когда мой код был не очень объектно-ориентированным, и я считаю, что я также тестировал внутренние детали, которые лучше всего оставить незафиксированными.

66
ответ дан jpmc26 19 August 2018 в 00:21
поделиться
  • 1
    Это было очень полезно для меня. Этот call_count сделал трюк для меня. Я +1 тебя. – Paul 16 September 2015 в 04:03
  • 2
    @ jpmc26 вы могли бы подробнее рассказать о своем редактировании? Что вы подразумеваете под словом 'best left unmocked'? Как еще вы проверили бы, был ли вызов выполнен в рамках метода – memo 15 December 2015 в 17:04
  • 3
    @memo Часто, чтобы позволить реальный метод быть вызванным. Если другой метод нарушен, он может нарушить тест, но значение этого избежать будет меньше, чем значение более простого и более удобного для пользователя теста. Лучшее время для фальсификации заключается в том, что внешний вызов другого метода , что вы хотите протестировать (обычно это означает, что в него передается какой-то результат, а тестируемый код не возвращает результат.) или другой метод имеет внешние зависимости (базу данных, веб-сайты), которые вы хотите устранить. (Технически, последний случай - скорее заглушка, и я бы с сомнением утверждал на нем.) – jpmc26 15 December 2015 в 17:32
  • 4
    @ jpmc26 mocking полезно, когда вы хотите избежать инъекции зависимостей или другого метода выбора стратегии выполнения. как вы упомянули, тестирование внутренней логики методов, без вызова внешних служб и, что более важно, без учета среды (нет нет для хорошего кода, чтобы иметь do() if TEST_ENV=='prod' else dont()), достигается легко, насмехаясь, как вы предлагали. побочным эффектом этого является поддержание тестов на версии (скажем, изменения кода между google-поиском api v1 и v2, ваш код проверяет версию 1 независимо от того, что) – Daniel Dubovski 27 October 2016 в 12:43
  • 5
    @ Даниэль Дубовски. Большинство ваших тестов должно быть основано на вводе / выводе. Это не всегда возможно, но если это невозможно в большинстве случаев, у вас, вероятно, проблема с дизайном. Когда вам нужно вернуть какое-то значение, которое обычно исходит из другой части кода, и вы хотите сократить зависимость, обычно будет работать заглушка. Mocks нужны только тогда, когда вам нужно проверить, что вызывающая функция изменения состояния (возможно, без возвращаемого значения). (Разница между макетом и заглушкой заключается в том, что вы не утверждаете при вызове с заглушкой.) Использование макетов, где будут выполняться заглушки, делает ваши тесты менее ремонтопригодными. – jpmc26 27 October 2016 в 17:12

Мне всегда нужно снова смотреть это снова и снова, так что вот мой ответ.

Утверждение нескольких вызовов методов на разных объектах одного и того же класса

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

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.

14
ответ дан Pedro M Duarte 19 August 2018 в 00:21
поделиться

assert_has_calls - это еще один подход к этой проблеме.

Из документов:

assert_has_calls (вызовы, any_order = False)

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

Если any_order имеет значение False (по умолчанию), вызовы должны быть последовательными. Дополнительные вызовы до или после указанных вызовов.

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

Пример:

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

105
ответ дан Pigueiras 19 August 2018 в 00:21
поделиться
  • 1
    Немного странно, что они решили добавить новый «вызов». тип, для которого они также могли использовать список или кортеж ... – jaapz 20 January 2015 в 11:36
  • 2
    @jaapz Это подклассы tuple: isinstance(mock.call(1), tuple) дает True. Они также добавили некоторые методы и атрибуты. – jpmc26 16 September 2015 в 04:50
  • 3
    Ранние версии Mock использовали простой кортеж, но это оказалось неудобно использовать. Каждый вызов функции получает кортеж (args, kwargs), поэтому, чтобы проверить, что «foo (123)» был вызван правильно, вам нужно «утверждать mock.call_args == ((123,), {})», что является полным по сравнению с «вызовом (123)», – Jonathan Hartley 25 January 2016 в 21:52
  • 4
    Что вы делаете, когда на каждом экземпляре вызова вы ожидаете другого возвращаемого значения? – CodeWithPride 8 August 2017 в 19:47
  • 5
    @CodeWithPride выглядит больше для работы side_effect – Pigueiras 8 August 2017 в 23:40
Другие вопросы по тегам:

Похожие вопросы: