Testing call order across mock objects with Mox and Python
I'm testing a function that obtains a skeleton object from one helper object, modifies it using a second helper, and passes the modified object back to the first helper. Something along the lines of:
class ReadModifyUpdate(object):
def __init__(self, store, modifier):
self._store = store
self._modifier = modifier
def modify(key):
record = self._store.read(key)
self._modifier.modify(record)
self._store.update(开发者_运维问答key, record)
Using Python and Mox, we can test this with:
class ReadModifyUpdateTest(mox.MoxTestBase):
def test_modify(self):
mock_record = self.mox.CreateMockAnthing()
mock_store = self.mox.CreateMockAnything()
mock_modifier = self.mox.CreateMockAnything()
mock_store.read("test_key").AndReturn(mock_record)
mock_modifier.modify(mock_record)
mock_store.update("test_key", mock_record)
self.mox.ReplayAll()
updater = ReadModifyUpdate(mock_store, mock_modifier)
updater.modify("test_key")
...but this doesn't catch the bug in which store.update() is inadvertently called before modifier.modify(). Is there a good way, in Mox, to check the order of methods called on multiple mocks? Something like EasyMock's MocksControl object?
Maybe not the best solution but you could try to use one mock that you give twice to your object under test. You then have control over the call order.
class ReadModifyUpdateTest(mox.MoxTestBase):
def test_modify(self):
mock_record = self.mox.CreateMockAnthing()
mock_storeModifier = self.mox.CreateMockAnything()
mock_storeModifier.read("test_key").AndReturn(mock_record)
mock_storeModifier.modify(mock_record)
mock_storeModifier.update("test_key", mock_record)
self.mox.ReplayAll()
updater = ReadModifyUpdate(mock_storeModifier, mock_storeModifier)
updater.modify("test_key")
To provide an answer to my own question - I've currently got this working using a side effect which checks the call order.
Defining a helper class:
class OrderedCallSequence(object):
def __init__(self, test_case):
self._expectation_count = 0
self._evaluated = 0
self._test_case = test_case
def assertOrder(self):
self._expectation_count += 1
expected_position = self._expectation_count
def side_effect(*args, **kwargs):
self._evaluated += 1
self._test_case.assertEquals(self._evaluated, expected_position,
msg="Invoked in incorrect sequence")
return side_effect
...the test case becomes:
class ReadModifyUpdateTest(mox.MoxTestBase):
def test_modify(self):
mock_record = self.mox.CreateMockAnthing()
mock_store = self.mox.CreateMockAnything()
mock_modifier = self.mox.CreateMockAnything()
sequence = OrderedCallSequence(self)
mock_store.read("test_key").WithSideEffects(sequence.assertOrder()).AndReturn(mock_record)
mock_modifier.modify(mock_record).WithSideEffects(sequence.assertOrder())
mock_store.update("test_key", mock_record).WithSideEffects(sequence.assertOrder())
self.mox.ReplayAll()
updater = ReadModifyUpdate(mock_store, mock_modifier)
updater.modify("test_key")
精彩评论