pythontestingmockingmox

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?


Solution

  • 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")