How can I use the Python mock
library to assert a specific sequence of calls to different mock objects?
For example, I want to assert:
foo(spam, eggs)
; then bar(beans, ham)
; then foo(sausage)
. I can patch each of foo
and bar
, and the resulting mock objects each allow me to make assertions about calls to that mock. But I need to access an overall sequence of calls to make assertions about that sequence.
Yes, ideally I would need only to inspect the resulting state and make assertion about it after the fact. But that's not feasible with some systems, and the only workable description of correct state is “these calls were made in this particular sequence”.
What capabilities of the mock
library can I use for accessing a sequence of calls to different objects, and asserting the calls were as expected in the right sequence?
Mock actually provides something like this builtin. Mocks frequently have a parent mock ... eg
somemock.foo # parent is somemock
the parent isn't directly exposed in the mock API, But the calls on the children are registered on the parent.
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')]
But, "My mocks don't all come from the same parent" you might be saying. All is not yet lost! You can create a parent and attach them to it after the fact.
parent_mock = mock.Mock()
parent_mock.attach_mock(foomock, 'foo')
parent_mock.attach_mock(barmock, 'bar')
and now you can do the same sort of asserting we did above (so long as you don't need to preserve the original mocks' parents ... then I'm not sure what to tell you...)
An initial attempt at solving this is with a specialised Mock
subclass which registers the calls in a provided sequence object, which can be whatever shared sequence you like.
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)
This has drawbacks:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.