Утвердить последовательность вызовов различных фиктивных объектов

Как я могу использовать библиотеку Python mock для утверждения определенной последовательности вызовов различных фиктивных объектов?

Например, я хочу утверждать:

  • Вызов foo(spam, eggs); тогда
  • Звонок bar(beans, ham); тогда
  • Вызов foo(sausage).

Я могу исправить каждый из foo и bar, и каждый из полученных фиктивных объектов позволяет мне делать утверждения о вызовах этой фиктивной модели. Но мне нужно получить доступ ко всей последовательности вызовов, чтобы делать утверждения об этой последовательности.

Да, в идеале мне нужно было бы только проверить результирующее состояние и сделать утверждение о нем постфактум. Но это невозможно для некоторых систем, и единственное приемлемое описание правильного состояния — «эти вызовы были сделаны в этой конкретной последовательности».

Какие возможности библиотеки mock я могу использовать для доступа к последовательности вызовов к различным объектам и подтверждения правильности последовательности вызовов?


person bignose    schedule 05.12.2014    source источник


Ответы (2)


Mock на самом деле предоставляет что-то вроде этого встроенного. Моки часто имеют родительский макет... например.

somemock.foo  # parent is somemock

родитель не отображается напрямую в фиктивном API, но вызовы дочерних элементов регистрируются в родительском.

import mock
m = mock.Mock()
m.a('hello world')
m.b('goodbye world')
m.c('kittens!')
m.a('Howdy')
m.assert_has_calls([
  mock.call.a('hello world'),
  mock.call.b('goodbye world'),
  mock.call.c('kittens!'),
  mock.call.a('Howdy')
])  # passes silently
m.assert_has_calls([
  mock.call.a('hello world'),
  mock.call.b('goodbye world'),
  mock.call.a('Howdy'),
  mock.call.c('kittens!')
]) # Error
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "/usr/local/lib/python2.7/dist-packages/mock.py", line 863, in assert_has_calls
#     'Actual: %r' % (calls, self.mock_calls)
# AssertionError: Calls not found.
# Expected: [call.a('hello world'), call.b('goodbye world'), call.a('Howdy'), call.c('kittens!')]
# Actual: [call.a('hello world'),
#  call.b('goodbye world'),
#  call.c('kittens!'),
#  call.a('Howdy')]

Но вы можете сказать: «Не все мои насмешки исходят от одного и того же родителя». Еще не все потеряно! Вы можете создать родителя и прикрепить их к нему постфактум.

parent_mock = mock.Mock()
parent_mock.attach_mock(foomock, 'foo')
parent_mock.attach_mock(barmock, 'bar')

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

person mgilson    schedule 05.12.2014

Первая попытка решить эту проблему заключается в специализированном подклассе Mock, который регистрирует вызовы в предоставленном объекте последовательности, который может быть любой общей последовательностью, которая вам нравится.

from copy import deepcopy
import mock

class CallRegisterMock(mock.MagicMock):
    """ A mock object that registers each call. """

    def __init__(self, call_register, *args, **kwargs):
        super(CallRegisterMock, self).__init__(*args, **kwargs)

        self.call_register = call_register

    def __call__(self, *args, **kwargs):
        args = deepcopy(args)
        kwargs = deepcopy(kwargs)
        call = mock.call(*args, **kwargs)
        qualified_call = (self, call)
        self.call_register.append(qualified_call)
        super(CallRegisterMock, self).__call__(*args, **kwargs)

Это имеет недостатки:

  • Это может быть повторное изобретение одного или нескольких колес. (Пожалуйста, добавьте лучший ответ, если вы так думаете.)
person bignose    schedule 05.12.2014